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 <[email protected]>
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).
-