Skip to content

Commit

Permalink
docs: add a nodejs tutorial (#397)
Browse files Browse the repository at this point in the history
The tutorial builds a simple "hello world" webapp and showcases using a
package-repository to install newer node.

Fixes #369
  • Loading branch information
tigarmo authored Nov 1, 2023
1 parent 2ef84cb commit b4dc8f1
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 0 deletions.
7 changes: 7 additions & 0 deletions docs/.wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ CLI
cmake
CMake
CMD
CTRL
declaratively
dev
Diátaxis
Expand All @@ -22,15 +23,20 @@ emacs
entrypoint
entrypoints
executables
expressjs
filesystem
fs
gc
GID
github
GPG
html
https
init
interoperable
io
js
json
Kubernetes
libc
libssl
Expand Down Expand Up @@ -75,6 +81,7 @@ SCons
skopeo
Skopeo
sliceName
snapd
snapcraft
Snapcraft
Snyk's
Expand Down
25 changes: 25 additions & 0 deletions docs/tutorials/code/node-app/rockcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: my-node-app
base: [email protected]
version: '1.0'
summary: A ROCK that bundles a simple nodejs app
description: |
This ROCK bundles a recent node runtime to serve a simple "hello-world" app.
license: GPL-3.0
platforms:
amd64:

services:
app:
override: replace
command: node server.js
startup: enabled
on-success: shutdown
on-failure: shutdown
working-dir: /lib/node_modules/node_web_app

parts:
app:
plugin: npm
npm-include-node: True
npm-node-version: "21.1.0"
source: src/
13 changes: 13 additions & 0 deletions docs/tutorials/code/node-app/src/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "node_web_app",
"version": "1.0.0",
"description": "Node.js on a ROCK",
"author": "First Last <[email protected]>",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
14 changes: 14 additions & 0 deletions docs/tutorials/code/node-app/src/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict';

const express = require('express')
const app = express()
const port = 8080
const host = '0.0.0.0'

app.get('/', (req, res) => {
res.send('Hello World from inside the ROCK!');
});

app.listen(port, host, () => {
console.log(`Running on http://${host}:${port}`);
});
50 changes: 50 additions & 0 deletions docs/tutorials/code/node-app/task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

summary: donejs tutorial

execute: |
# [docs:install-rockcraft]
sudo snap install rockcraft --classic --edge
# [docs:install-rockcraft-end]
# [docs:build-rock]
rockcraft pack
# [docs:build-rock-end]
# [docs:check-rock]
ls my-node-app_1.0_amd64.rock
# [docs:check-rock-end]
# test container execution
docker images
# [docs:skopeo-copy]
sudo /snap/rockcraft/current/bin/skopeo --insecure-policy copy oci-archive:my-node-app_1.0_amd64.rock docker-daemon:my-node-app:1.0
# [docs:skopeo-copy-end]
rm *.rock
docker images my-node-app:1.0
# When the container is started it launches the app listening on port 8080
# NOTE: the convoluted code here is to expose the "docker run" line cleanly
# to the documentation that uses it.
cat <<EOF > run-container.sh
# [docs:run-container]
docker run --name my-node-app -p 8000:8080 my-node-app:1.0
# [docs:run-container-end]
EOF
bash run-container.sh &
# Wait for a bit for the app to come online
retry -n 5 --wait 2 curl localhost:8000
curl localhost:8000 | MATCH "Hello World from inside the ROCK"
# [docs:stop-container]
docker stop my-node-app
# [docs:stop-container-end]
restore: |
rm -f ./*.rock
docker rm -f my-node-app
docker rmi -f my-node-app:1.0
1 change: 1 addition & 0 deletions docs/tutorials/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ reproducible steps.
Chisel packages into a ROCK <chisel.rst>
Publish a ROCK <publish-a-rock.rst>
Migrate a Docker image to a Chiselled ROCK <migrate-to-chiselled-rock.rst>
Bundle a Node.js app into a ROCK <node-app.rst>
156 changes: 156 additions & 0 deletions docs/tutorials/node-app.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
Bundle a Node.js app into a ROCK
********************************

This tutorial describes the steps needed to bundle a typical Node.js application
into a ROCK.

Prerequisites
-------------
- snap enabled system (https://snapcraft.io/docs/installing-snapd)
- LXD installed
(https://documentation.ubuntu.com/lxd/en/latest/installing/)
- Docker installed (https://snapcraft.io/docker)
- a text editor


Install Rockcraft
-----------------

Install Rockcraft on your host:

.. literalinclude:: code/node-app/task.yaml
:language: bash
:start-after: [docs:install-rockcraft]
:end-before: [docs:install-rockcraft-end]
:dedent: 2


Project Setup
-------------

Starting in an empty folder, create a ``src/`` subdirectory. Inside it, add two
files:

The first one is the ``package.json`` listing of dependencies, with the
following contents:

.. literalinclude:: code/node-app/src/package.json
:caption: package.json
:language: json

The second file is our sample app, a simple "hello world" server. Still inside
``src/``, add the following contents to ``server.js``:

.. literalinclude:: code/node-app/src/server.js
:caption: server.js
:language: javascript

Next, we'll setup the Rockcraft project. In the original empty folder, create
an empty file called ``rockcraft.yaml``. Then add the following snippets, one
after the other:

Add the metadata that describes your ROCK, such as its name and licence:

.. literalinclude:: code/node-app/rockcraft.yaml
:caption: rockcraft.yaml
:language: yaml
:start-at: name: my-node-app
:end-at: amd64:

Add the container entrypoint, as a `Pebble`_ service:

.. literalinclude:: code/node-app/rockcraft.yaml
:caption: rockcraft.yaml
:language: yaml
:start-at: services:
:end-at: working-dir: /lib/node_modules/node_web_app

Finally, add a part that describes how to build the app created in the ``src/``
directory using the ``npm`` plugin:

.. literalinclude:: code/node-app/rockcraft.yaml
:caption: rockcraft.yaml
:language: yaml
:start-at: parts:
:end-at: source: src/

The whole file then looks like this:

.. literalinclude:: code/node-app/rockcraft.yaml
:caption: rockcraft.yaml
:language: yaml


Pack the ROCK with Rockcraft
----------------------------

To build the ROCK, run:

.. literalinclude:: code/node-app/task.yaml
:language: bash
:start-after: [docs:build-rock]
:end-before: [docs:build-rock-end]
:dedent: 2

At the end of the process, a new ROCK file should be present in the current
directory:

.. literalinclude:: code/node-app/task.yaml
:language: bash
:start-after: [docs:check-rock]
:end-before: [docs:check-rock-end]
:dedent: 2

Run the ROCK in Docker
----------------------

First, import the recently created ROCK into Docker:

.. literalinclude:: code/node-app/task.yaml
:language: bash
:start-after: [docs:skopeo-copy]
:end-before: [docs:skopeo-copy-end]
:dedent: 2

Since the ROCK bundles a web-app, we'll first start serving that app on local
port 8000:

.. literalinclude:: code/node-app/task.yaml
:language: bash
:start-after: [docs:run-container]
:end-before: [docs:run-container-end]
:dedent: 2

The output will look similar to this, indicating that Pebble started the ``app``
service:

.. code-block:: text
2023-10-30T12:37:33.654Z [pebble] Started daemon.
2023-10-30T12:37:33.659Z [pebble] POST /v1/services 3.878846ms 202
2023-10-30T12:37:33.659Z [pebble] Started default services with change 1.
2023-10-30T12:37:33.663Z [pebble] Service "app" starting: node server.js
2023-10-30T12:37:33.864Z [app] Running on http://0.0.0.0:8080
Next, open your web browser and navigate to ``http://localhost:8000``. You
should see a blank page with a "Hello World from inside the ROCK!" message.
Success!

You can now stop the running container by either interrupting it with CTRL+C or
by running the following in another terminal:

.. literalinclude:: code/node-app/task.yaml
:language: bash
:start-after: [docs:stop-container]
:end-before: [docs:stop-container-end]
:dedent: 2

References
----------

The sample app code comes from the "Hello world example" Express tutorial,
available at https://expressjs.com/en/starter/hello-world.html.


.. _`Pebble`: https://github.com/canonical/pebble
.. _`NodeSource`: https://nodesource.com/

0 comments on commit b4dc8f1

Please sign in to comment.