This is an automated email from the ASF dual-hosted git repository. jamesthomas pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git
The following commit(s) were added to refs/heads/master by this push: new 5a40820 Updating Node.js and Docker docs. (#4451) 5a40820 is described below commit 5a408201a1afac72785d96d10ecbf3e114783c5e Author: James Thomas <jthomas...@gmail.com> AuthorDate: Tue Apr 23 11:41:43 2019 +0100 Updating Node.js and Docker docs. (#4451) * Updating Node.js and Docker docs. Adding common use-case instructions based on community feedback. * Fixing scancode issues --- docs/actions-docker.md | 435 +++++++++++++++++++++++++++++++++---------------- docs/actions-nodejs.md | 317 ++++++++++++++++++++++++----------- 2 files changed, 515 insertions(+), 237 deletions(-) diff --git a/docs/actions-docker.md b/docs/actions-docker.md index 84ce543..5d53a1c 100644 --- a/docs/actions-docker.md +++ b/docs/actions-docker.md @@ -19,149 +19,298 @@ ## Creating and invoking Docker actions -With OpenWhisk Docker actions, you can write your actions in any language, bundle larger -or complex dependencies, and tailor the runtime environment to suite your needs. - -- As a prerequisite, you must have a Docker Hub account. - To set up a free Docker ID and account, go to [Docker Hub](https://hub.docker.com). -- The easiest way to get started with a Docker action is to use the OpenWhisk Docker skeleton. - - You can install the skeleton with the `wsk sdk install docker` CLI command. - - This is a a Docker image based on [python:3.6.1-alpine](https://hub.docker.com/r/library/python). - - The skeleton requires an entry point located at `/action/exec` inside the container. - This may be an executable script or binary compatible with this distribution. - - The executable receives the input arguments via a single command-line argument string - which can be deserialized as a `JSON` object. - It must return a result via `stdout` as a single-line string of serialized `JSON`. - - You may include any compilation steps or dependencies by modifying the `Dockerfile` - included in the `dockerSkeleton`. - -The instructions that follow show you how to use the OpenWhisk Docker skeleton. - -1. Install the Docker skeleton. - - ``` - wsk sdk install docker - ``` - ``` - The Docker skeleton is now installed at the current directory. - ``` - - ``` - $ ls dockerSkeleton/ - ``` - ``` - Dockerfile README.md buildAndPush.sh example.c - ``` - - The skeleton is a Docker container template where you can inject your code in the form of custom binaries. - -2. Create the executable. The Docker skeleton includes a C program that you can use as an example. - - ``` - cat dockerSkeleton/example.c - ``` - ```c - #include <stdio.h> - int main(int argc, char *argv[]) { - printf("This is an example log message from an arbitrary C program!\n"); - printf("{ \"msg\": \"Hello from arbitrary C program!\", \"args\": %s }", - (argc == 1) ? "undefined" : argv[1]); - } - ``` - - You can modify this file as needed, or, add additional code and dependencies to the Docker image. - In case of the latter, you may need to tweak the `Dockerfile` as necessary to build your executable. - The binary must be located inside the container at `/action/exec`. - - The executable receives a single argument from the command line. It is a string serialization of the JSON - object representing the arguments to the action. The program may log to `stdout` or `stderr`. - By convention, the last line of output _must_ be a stringified JSON object which represents the result of - the action. - -3. Build the Docker image and upload it using a supplied script. - You must first run `docker login` to authenticate, and then run the script with a chosen image name. - - ``` - docker login -u janesmith -p janes_password - ``` - ``` - cd dockerSkeleton - ``` - ``` - ./buildAndPush.sh janesmith/blackboxdemo - ``` - - Notice that part of the example.c file is compiled as part of the Docker image build process, - so you do not need C compiled on your machine. - In fact, unless you are compiling the binary on a compatible host machine, it may not run inside - the container since formats will not match. - - Your Docker container may now be used as an OpenWhisk action. - - ``` - wsk action create example --docker janesmith/blackboxdemo - ``` - - Notice the use of `--docker` when creating an action. Currently all Docker images are assumed - to be hosted on Docker Hub. - - *Note:* It is considered best-practice for production images to be versioned via docker image tags. The absence of a tag will be treated the same as using a "latest" tag, which will result in a pull from the registry when creating new containers. Pulling an image may fail due to a network interruption or Docker Hub outage. For tagged images however, the system will gracefully recover from a failed pull by using the image that is already locally available, making it much more resilient [...] - - The "latest" tag should therefore only be used for rapid prototyping where guaranteeing the latest code state is more important than runtime stability at scale. - - Please also note that "latest" doesn't mean newest tag, but rather "latest" is an alias for any image built without an explicit tag. - -## Invoking a Docker action - -Docker actions are invoked as [any other OpenWhisk action](actions.md#the-basics). - - ``` - wsk action invoke --result example --param payload Rey - ``` - ```json - { - "args": { - "payload": "Rey" - }, - "msg": "Hello from arbitrary C program!" - } - ``` - -## Updating a Docker action - -To update the Docker action, run buildAndPush.sh again _and_ update your action. -This will upload the latest image to Docker Hub and force the system to create a new container based on the image. -If you do not update the action, then the image is pulled when there are no warm containers available for your action. -A warm container will continue using a previous version of your Docker image, -and any new invocations of the action will continue to use that image unless you run `wsk action update`. -This will indicate to the system that for new invocations it should execute a docker pull to get your new Docker image. - - ``` - ./buildAndPush.sh janesmith/blackboxdemo - ``` - ``` - wsk action update example --docker janesmith/blackboxdemo - ``` - -**Note:** As noted above, only images with the tag "latest" or no tag will guarantee to be pulled again, even after updating the action. Any other tag might fall back to use the old image for stability reasons. - -To force an updated image after an action update, consider using versioned tags on the image. +Apache OpenWhisk supports using custom Docker images as the action runtime. Custom runtimes images can either have the action source files built-in or injected dynamically by the platform during initialisation. + +Building custom runtime images is a common solution to the issue of having external application dependencies too large to deploy, due to the action size limit (48MB), e.g. machine learning libraries. + +### Usage + +The [Apache OpenWhisk CLI](https://github.com/apache/incubator-openwhisk-cli) has a `--docker` configuration parameter to set a custom runtime for an action. + +``` +wsk action create <ACTION_NAME> --docker <IMAGE> source.js +``` + +*`<IMAGE>` must be an image name for a public Docker image on [Docker Hub](https://hub.docker.com/search?q=&type=image).* + +The `--docker` flag can also be used without providing additional source or archive files for an action. + +``` +wsk action create <ACTION_NAME> --docker <IMAGE> +``` + +In this scenario, action source code will not be injected into the runtime during cold-start initialisation. The runtime container must handle the platform invocation requests directly. + +### Restrictions + +- Custom runtime images must implement the [Action interface](https://github.com/apache/incubator-openwhisk/blob/master/docs/actions-new.md#runtime-general-requirements). This is the [protocol used by the platform](https://github.com/apache/incubator-openwhisk/blob/master/docs/actions-new.md#action-interface) to pass invocation requests to the runtime containers. Containers are expected to expose a HTTP server (running on port 8080) with `/init` and `/run` endpoints. +- Custom runtime images must be available on [Docker Hub](https://hub.docker.com/search?q=&type=image). Docker Hub is the only container registry currently supported. This means all custom runtime images will need to be publicly available. +- Custom runtime images will be pulled from Docker Hub into the local platform registry upon the first invocation. This can lead to longer cold-start times on the first invocation with a new or updated image. Once images have been pulled down, they are cached locally. + +### Image Refresh Behaviour + +Custom runtimes images should be versioned using explicit [image tags](https://docs.docker.com/engine/reference/commandline/tag/) where possible. + +When an image identifier has the `latest` tag (or has no explicit tag), creating new runtime containers from a custom image will always result in an image refresh check against the registry. Pulling an image may fail due to a network interruption or Docker Hub outage. Explicitly tagged images allow the system to gracefully recover by using locally cached images, making it much more resilient against external issues. + +The "latest" tag should only be used for rapid prototyping, where guaranteeing the latest code state is more important than runtime stability at scale. + +### Existing Runtime Images + +Apache OpenWhisk publishes all the [existing runtime images](https://hub.docker.com/u/openwhisk) on Docker Hub. This makes it simple to extend an existing runtimes with additional libraries or native dependencies. Public runtimes images can be used as base images in new runtime images. + +Here are some of the more common runtime images... + +- `openwhisk/action-nodejs-v10` - [Node.js 10](https://hub.docker.com/r/openwhisk/action-nodejs-v10) ([Source](https://github.com/apache/incubator-openwhisk-runtime-nodejs/blob/master/core/nodejs10Action/Dockerfile)) +- `openwhisk/python3action` - [Python 3](https://hub.docker.com/r/openwhisk/python3action) ([Source](https://github.com/apache/incubator-openwhisk-runtime-python/blob/master/core/pythonAction/Dockerfile)) +- `openwhisk/java8action` - [Java 8](https://hub.docker.com/r/openwhisk/java8action) ([Source](https://github.com/apache/incubator-openwhisk-runtime-java/blob/master/core/java8/Dockerfile)) +- `openwhisk/action-swift-v4.2` - [Swift 4.2](https://hub.docker.com/r/openwhisk/action-swift-v4.2) ([Source](https://github.com/apache/incubator-openwhisk-runtime-swift/blob/master/core/swift42Action/Dockerfile)) +- `openwhisk/action-php-v7.3` - [PHP 7.3](https://hub.docker.com/r/openwhisk/action-php-v7.3) ([Source](https://github.com/apache/incubator-openwhisk-runtime-php/blob/master/core/php7.3Action/Dockerfile)) +- `openwhisk/action-ruby-v2.5` - [Ruby 2.5](https://hub.docker.com/r/openwhisk/action-ruby-v2.5) ([Source](https://github.com/apache/incubator-openwhisk-runtime-ruby/blob/master/core/ruby2.5Action/Dockerfile)) + +## Extending Existing Runtimes + +If you want to use extra libraries in an action that can't be deployed (due to the action size limit), building a custom runtime with a project image runtime base image is an easy way to handle this. + +By using an existing language runtime image as the base image, the container will already be set up to handle platform invocation requests for that language. This means the image build file only needs to contain commands to install those extra libraries or dependencies + +Here are examples for Node.js and Python using this approach to provide large libraries in the runtime. + +### Node.js + +[Tensorflow.js](https://www.tensorflow.org/js/) is a JavaScript implementation of [TensorFlow](https://www.tensorflow.org/), the open-source Machine Learning library from Google. This project also comes with a [Node.js backend driver](https://github.com/tensorflow/tfjs-node) to run the project on CPU or GPU devices in the Node.js runtime. + +Both the core library and CPU backend driver for Node.js (`tfjs` and `tfjs-node`) can be installed as normal NPM packages. Unfortunately, it is not possible to deploy these libraries in a zip file to the Node.js runtime in Apache OpenWhisk due to the size of the library and its dependencies. The `tfjs-node` library has a native dependency which is over 170MB. + +Instead, we can build a custom runtime which extends the project's Node.js runtime image and runs `npm install` during the container build process. These libraries will then be pre-installed into the runtime and can be excluded from the deployment archive. + +- Create a `Dockerfile` with the following contents: + +``` +FROM openwhisk/action-nodejs-v10:latest + +RUN npm install @tensorflow/tfjs-node +``` + +- Build the Docker image. + +``` +docker build -t action-nodejs-v10:tf-js . +``` + +- Tag the Docker image with your Docker Hub username. + +``` +docker tag action-nodejs-v10:tf-js <USER_NAME>/action-nodejs-v10:tf-js +``` + +- Push the Docker image to Docker Hub. + +``` +docker push <USER_NAME>/action-nodejs-v10:tf-js +``` + +- Create a new Apache OpenWhisk action with the following source code: + +```javascript +const tf = require('@tensorflow/tfjs-node') + +const main = () => { + return { tf: tf.version } +} +``` + +``` +wsk action create tfjs --docker <USER_NAME>/action-nodejs-v10:tf-js action.js +``` + +- Invoking the action should return the TensorFlow.js libraries versions available in the runtime. + +``` +wsk action invoke tfjs --result +``` + +``` +{ + "tf": { + "tfjs": "1.0.4", + "tfjs-converter": "1.0.4", + "tfjs-core": "1.0.4", + "tfjs-data": "1.0.4", + "tfjs-layers": "1.0.4", + "tfjs-node": "1.0.3" + } +} +``` + +### Python + +Python is a popular language for machine learning and data science due to availability of libraries like [numpy](http://www.numpy.org/). + +Python libraries can be [imported into the Python runtime](https://github.com/apache/incubator-openwhisk/blob/master/docs/actions-python.md#packaging-python-actions-with-a-virtual-environment-in-zip-files) in Apache OpenWhisk by including a `virtualenv` folder in the deployment archive. This approach does not work when the deployment archive would be larger than the action size limit (48MB). + +Instead, we can build a custom runtime which extends the project's Python runtime image and runs `pip install` during the container build process. These libraries will then be pre-installed into the runtime and can be excluded from the deployment archive. + +- Create a `Dockerfile` with the following contents: + +``` +FROM openwhisk/python3action:latest + +RUN apk add --update py-pip +RUN pip install numpy +``` + +- Build the Docker image. + +``` +docker build -t python3action:ml-libs . +``` + +- Tag the Docker image with your Docker Hub username. + +``` +docker tag python3action:ml-libs <USER_NAME>/python3action:ml-libs +``` + +- Push the Docker image to Docker Hub. + +``` +docker push <USER_NAME>/python3action:ml-libs +``` + +- Create a new Apache OpenWhisk action with the following source code: + +```python +import numpy + +def main(params): + return { + "numpy": numpy.__version__ + } +``` + +``` +wsk action create ml-libs --docker <USER_NAME>/python3action:ml-libs action.py +``` + +- Invoking the action should return the TensorFlow.js library versions available in the runtime. + +``` +wsk action invoke ml-libs --result +``` + +``` +{ + "numpy": "1.16.2" +} +``` ## Creating native actions -Docker actions accept initialization data via a (zip) file, similar to other actions kinds. -For example, the tutorial above created a binary executable inside the container located at `/action/exec`. -If you copy this file to your local file system and zip it into `exec.zip` then you can use the following -commands to create a docker action which receives the executable as initialization data. - - ```bash - wsk action create example exec.zip --native - ``` - which is equivalent to the following command. - ```bash - wsk action create example exec.zip --docker openwhisk/dockerskeleton - ``` - -Using `--native`, you can see that any executable may be run as an OpenWhisk action. -This includes `bash` scripts, or cross compiled binaries. For the latter, the constraint -is that the binary must be compatible with the `openwhisk/dockerskeleton` image. +Docker support can also be used to run any executable file (from static binaries to shell scripts) on the platform. Executable files need to use the `openwhisk/dockerskeleton` [runtime image](https://github.com/apache/incubator-openwhisk-runtime-docker). Native actions can be created using the `--native` CLI flag, rather than explicitly specifying `dockerskeleton` as the runtime image name. + +### Usage + +``` +wsk action create my-action --native source.sh +wsk action create my-action --native archive.zip +``` + +Executables can either be text or binary files. Text-based executable files (e.g. shell scripts) are passed directly as the action source files. Binary files (e.g. C programs) must be named `exec` and packaged into a zip archive. + +Native action source files must be executable within the `openwhisk/dockerskeleton` [runtime image](https://github.com/apache/incubator-openwhisk-runtime-docker). This means being compiled for the correct platform architecture, linking to the correct dynamic libraries and using pre-installed external dependencies. + +When an invocation request is received by the runtime container, the native action file will be executed until the process exits. Action invocation parameters will be passed as a JSON string to `stdin`. + +When the process ends, the last line of text output to `stdout` will be parsed as the action result. This must contain a text string with a JSON object. All other text lines written to `stdout` will be treated as logging output and returned into the response logs for the activation. + +### Example (Shell Script) + +- Create a shell script called `script.sh` with the following contents. + +``` +#!/bin/bash +read ARGS +NAME=`echo "$ARGS" | jq -r '."name"'` +DATE=`date` +echo "{ \"message\": \"Hello $NAME! It is $DATE.\" }" +``` + +- Create an action from this shell script. + +``` + wsk action create bash script.sh --native +``` + +- Invoke the action with the `name` parameter. + +``` +wsk action invoke bash --result --param name James +``` + +``` +{ + "message": "Hello James! It is Thu Apr 18 15:24:23 UTC 2019." +} +``` + +### Example (Static C Binary) + +- Create a C source file called `main.c` with the following contents: + +```c +#include <stdio.h> +int main(int argc, char *argv[]) { + printf("This is an example log message from an arbitrary C program!\n"); + printf("{ \"msg\": \"Hello from arbitrary C program!\", \"args\": %s }", + (argc == 1) ? "undefined" : argv[1]); +} +``` + +- Create a shell script (`build.sh`) with the following contents: + +``` +#!/bin/bash +apk add gcc libc-dev + +gcc main.c -o exec +``` + +- Make the build script executable. + +``` +chmod 755 build.sh +``` + +- Compile the C binary using the `dockerskeleton` image and the build script. + +``` +docker run -it -v $PWD:/action/ -w /action/ openwhisk/dockerskeleton ./build.sh +``` + +- Add the binary to a zip file. + +``` +zip -r action.zip exec +``` + +- Create an action from the zip file containing the binary. + +``` + wsk action create c-binary action.zip --native +``` + +- Invoke the action with the `name` parameter. + +``` +wsk action invoke c-binary --result --param name James +``` + +``` +{ + "args": { + "name": "James" + }, + "msg": "Hello from arbitrary C program!" +} +``` diff --git a/docs/actions-nodejs.md b/docs/actions-nodejs.md index ff6434e..9c76149 100644 --- a/docs/actions-nodejs.md +++ b/docs/actions-nodejs.md @@ -104,7 +104,7 @@ Datetime Activation ID Kind Start Duration "end": 1552762005048, ... } - ``` + ``` Comparing the `start` and `end` time stamps in the activation record, you can see that this activation took slightly over two seconds to complete. @@ -166,11 +166,17 @@ This example invokes a Yahoo Weather service to get the current conditions at a This example also passed a parameter to the action by using the `--param` flag and a value that can be changed each time the action is invoked. Find out more about parameters in the [Working with parameters](./parameters.md) section. -## Packaging an action as a Node.js module +## Packaging actions as Node.js modules with NPM libraries + +Instead of writing all your action code in a single JavaScript source file, actions can be deployed from a zip file containing a [Node.js module](https://nodejs.org/docs/latest-v10.x/api/modules.html#modules_modules). + +Archive zip files are extracted into the runtime environment and dynamically imported using `require()` during initialisation. **Actions packaged as a zip file MUST contain a valid `package.json` with a `main` field used to denote the [module index file](https://nodejs.org/docs/latest-v10.x/api/modules.html#modules_folders_as_modules) to return.** -As an alternative to writing all your action code in a single JavaScript source file, you can write an action as a `npm` package. Consider as an example a directory with the following files: +Including a `node_modules` folder in the zip file means external NPM libraries can be used on the platform. -First, `package.json`: +### Simple Example + +- Create the following `package.json` file: ```json { @@ -182,7 +188,7 @@ First, `package.json`: } ``` -Then, `index.js`: +- Create the following `index.js` file: ```javascript function myAction(args) { @@ -194,132 +200,261 @@ function myAction(args) { exports.main = myAction; ``` -Note that the action is exposed through `exports.main`; the action handler itself can have any name, as long as it conforms to the usual signature of accepting an object and returning an object (or a `Promise` of an object). Per Node.js convention, you must either name this file `index.js` or specify the file name you prefer as the `main` property in package.json. +Functions are exported from a module by setting properties on the `exports` object. The `--main` property on the action can be used to configure the module function invoked by the platform (this defaults to `main`). -To create an OpenWhisk action from this package: +- Install module dependencies using NPM. -1. Install first all dependencies locally +``` +npm install +``` - ``` - $ npm install - ``` +- Create a `.zip` archive containing all files (including all dependencies). -2. Create a `.zip` archive containing all files (including all dependencies): +``` +zip -r action.zip * +``` - ``` - $ zip -r action.zip * - ``` +> Please note: Using the Windows Explorer action for creating the zip file will result in an incorrect structure. OpenWhisk zip actions must have `package.json` at the root of the zip, while Windows Explorer will put it inside a nested folder. The safest option is to use the command line `zip` command as shown above. - > Please note: Using the Windows Explorer action for creating the zip file will result in an incorrect structure. OpenWhisk zip actions must have `package.json` at the root of the zip, while Windows Explorer will put it inside a nested folder. The safest option is to use the command line `zip` command as shown above. +- Create the action from the zip file. -3. Create the action: +``` +wsk action create packageAction --kind nodejs:10 action.zip +``` - ``` - wsk action create packageAction --kind nodejs:10 action.zip - ``` +When creating an action from a `.zip` archive with the CLI tool, you must explicitly provide a value for the `--kind` flag by using `nodejs:10`, `nodejs:8` or `nodejs:6`. + +- Invoke the action as normal. + +``` +wsk action invoke --result packageAction --param lines "[\"and now\", \"for something completely\", \"different\" ]" +``` +```json +{ + "padded": [ + ".......................and now", + "......for something completely", + ".....................different" + ] +} +``` - When creating an action from a `.zip` archive with the CLI tool, you must explicitly provide a value for the `--kind` flag by using `nodejs:10`, `nodejs:8` or `nodejs:6`. +### Handling NPM Libraries with Native Dependencies -4. You can invoke the action like any other: +Node.js libraries can import native dependencies needed by the modules. These native dependencies are compiled upon installation to ensure they work in the local runtime. Native dependencies for NPM libraries must be compiled for the correct platform architecture to work in Apache OpenWhisk. - ``` - wsk action invoke --result packageAction --param lines "[\"and now\", \"for something completely\", \"different\" ]" - ``` - ```json - { - "padded": [ - ".......................and now", - "......for something completely", - ".....................different" - ] - } - ``` +There are two approaches to using libraries with native dependencies... -Finally, note that while most `npm` packages install JavaScript sources on `npm install`, some also install and compile binary artifacts. The archive file upload currently does not support binary dependencies but rather only JavaScript dependencies. Action invocations may fail if the archive includes binary dependencies. +1. Run `npm install` inside a Docker container from the platform images. +2. Building custom runtime image with libraries pre-installed. -### Package an action as a single bundle +**The first approach is easiest but can only be used when a zip file containing all source files and libraries is less than the action size limit (48MB).** -It is convenient to only include the minimal code into a single `.js` file that includes dependencies. This approach allows for faster deployments, and in some circumstances where packaging the action as a zip might be too large because it includes unnecessary files. +#### Running `npm install` inside runtime container -You can use a JavaScript module bundler such as [webpack](https://webpack.js.org/concepts/). When webpack processes your code, it recursively builds a dependency graph that includes every module that your action needs. + - Run the following command to bind the local directory into the runtime container and run `npm install`. -Here is a quick example using webpack: +``` +docker run -it -v $PWD:/nodejsAction openwhisk/action-nodejs-v10 "npm install" +``` + This will leave a `node_modules` folder with native dependencies compiled for correct runtime. -Taking the previous example `package.json` add `webpack` as a development dependency and add some npm script commands. -```json -{ - "name": "my-action", - "main": "dist/bundle.js", - "scripts": { - "build": "webpack --config webpack.config.js", - "deploy": "wsk action update my-action dist/bundle.js --kind nodejs:8" - }, - "dependencies": { - "left-pad": "1.1.3" + - Zip up the action source files including `node_modules` directory. + +``` +zip -r action.zip * +``` + +- Create new action with action archive. + +``` +ibmcloud wsk action create my-action --kind nodejs:10 action.zip +``` + +#### Building custom runtime image + +- Create a `Dockerfile` with the `npm install` command run during build. + +``` +FROM openwhisk/action-nodejs-v10 + +RUN npm install <LIB_WITH_NATIVE_DEPS> +``` + +- Build and push the image to Docker Hub. + +``` +$ docker build -t <USERNAME>/custom-runtime . +$ docker push <USERNAME>/custom-runtime +``` + +- Create new action using custom runtime image. + +``` +ibmcloud wsk action create my-action --docker <USERNAME>/custom-runtime action.zip +``` + +**Make sure the `node_modules` included in the `action.zip` does not include the same libraries folders.** + +## Using JavaScript Bundlers to package action source files + +Using a JavaScript module bundler can transform application source files (with external dependencies) into a single compressed JavaScript file. This can lead to faster deployments, lower cold-starts and allow you to deploy large applications where individual sources files in a zip archive are larger than the action size limit. + +Here are the instructions for how to use three popular module bundlers with the Node.js runtime. The "left pad" action example will be used as the source file for bundling along with the external library. + +### Using rollup.js ([https://rollupjs.org](https://rollupjs.org)) + +- Re-write the `index.js` to use ES6 Modules, rather than CommonJS module format. + +```javascript +import leftPad from 'left-pad'; + +function myAction(args) { + const lines = args.lines || []; + return { padded: lines.map(l => leftPad(l, 30, ".")) } +} + +export const main = myAction +``` + +*Make sure you export the function using the `const main = ...` pattern. Using `export {myAction as main}` does not work due to tree-shaking. See this [blog post](https://boneskull.com/rollup-for-javascript-actions-on-openwhisk/) for full details on why this is necessary.* + +- Create the Rollup.js configuration file in `rollup.config.js` with the following contents. + +```javascript +import commonjs from 'rollup-plugin-commonjs'; +import resolve from 'rollup-plugin-node-resolve'; + +export default { + input: 'index.js', + output: { + file: 'bundle.js', + format: 'cjs' }, - "devDependencies": { - "webpack": "^3.8.1" - } + plugins: [ + resolve(), + commonjs() + ] +}; +``` + +- Install the Rollup.js library and plugins using NPM. + +``` +npm install rollup rollup-plugin-commonjs rollup-plugin-node-resolve --save-dev +``` + +- Run the Rollup.js tool using the configuration file. + +``` +npx rollup --config +``` + +- Create an action using the bundle source file. + +``` +wsk action create my-action bundle.js --kind nodejs:10 +``` + +- Invoke the action as normal. Results should be the same as the example above. + +``` +wsk action invoke my-action --result --param lines "[\"and now\", \"for something completely\", \"different\" ]" +``` + +### Using webpack ([https://webpack.js.org/](https://webpack.js.org/)) + +- Change `index.js` to export the `main` function using as a global reference. + +```javascript +const leftPad = require('left-pad'); + +function myAction(args) { + const lines = args.lines || []; + return { padded: lines.map(l => leftPad(l, 30, ".")) } } + +global.main = myAction ``` -Create the webpack configuration file `webpack.config.js`. +This allows the bundle source to "break out" of the closures Webpack uses when defining the modules. + +- Create the Webpack configuration file in `webpack.config.js` with the following contents. + ```javascript -var path = require('path'); module.exports = { entry: './index.js', + target: 'node', output: { - path: path.resolve(__dirname, 'dist'), filename: 'bundle.js' - }, - target: 'node' + } }; ``` -Set the variable `global.main` to the main function of the action. -From the previous example: +- Install the Webpack library and CLI using NPM. + +``` +npm install webpack-cli --save-dev +``` + +- Run the Webpack tool using the configuration file. + +``` +npx webpack --config webpack.config.js +``` + +- Create an action using the bundle source file. + +``` +wsk action create my-action dist/bundle.js --kind nodejs:10 +``` + +- Invoke the action as normal. Results should be the same as the example above. + +``` +wsk action invoke my-action --result --param lines "[\"and now\", \"for something completely\", \"different\" ]" +``` + +### Using parcel ([https://parceljs.org/](https://parceljs.org/)) + +- Change `index.js` to export the `main` function using as a global reference. + ```javascript +const leftPad = require('left-pad'); + function myAction(args) { - const leftPad = require("left-pad") - const lines = args.lines || []; - return { padded: lines.map(l => leftPad(l, 30, ".")) } + const lines = args.lines || []; + return { padded: lines.map(l => leftPad(l, 30, ".")) } } -global.main = myAction; -``` -If your function name is `main`, use this syntax instead: -```javascript -global.main = main; +global.main = myAction ``` -To build and deploy an OpenWhisk Action using `npm` and `webpack`: +This allows the bundle source to "break out" of the closures Parcel uses when defining the modules. -1. First, install dependencies locally: +- Install the Parcel library using NPM. - ``` - npm install - ``` +``` +npm install parcel-bundler --save-dev +``` -2. Build the webpack bundle: +- Run the Parcel tool using the configuration file. - ``` - npm run build - ``` +``` + npx parcel index.js +``` - The file `dist/bundle.js` is created, and is used to deploy as the Action source code. +- Create an action using the bundle source file. -3. Create the Action using the `npm` script or the CLI. - Using `npm` script: - ``` - npm run deploy - ``` +``` +wsk action create my-action dist/index.js --kind nodejs:10 +``` - Using the CLI: - ``` - wsk action update my-action dist/bundle.js - ``` +- Invoke the action as normal. Results should be the same as the example above. -Finally, the bundle file that is built by `webpack` doesn't support binary dependencies but rather JavaScript dependencies. So Action invocations will fail if the bundle depends on binary dependencies, because this is not included with the file `bundle.js`. +``` +wsk action invoke my-action --result --param lines "[\"and now\", \"for something completely\", \"different\" ]" +``` ## Reference @@ -397,9 +532,3 @@ The Node.js version 10.13.0 environment is used if the `--kind` flag is explicit The following packages are pre-installed in the Node.js version 10 environment: - [openwhisk v3.18.0](https://www.npmjs.com/package/openwhisk) - JavaScript client library for the OpenWhisk platform. Provides a wrapper around the OpenWhisk APIs. - -### Packaging npm packages with your actions -For any `npm` packages that are not pre-installed in the Node.js environment, you can bundle them as dependencies when you create or update your action. - -For more information, see [Packaging an action as a Node.js module](./actions.md#packaging-an-action-as-a-nodejs-module) or [Packaging an action as a single bundle](./actions.md#packaging-an-action-as-a-single-bundle). -