James Thomas

Notes on JavaScript

Node.js V4 in Cloud Foundry

Last week, Node.js released the latest version of their project, v4.0.0. This release, representing the convergence of io.js with the original Node.js project, came with lots of exciting features like improved ES6 support.

Cloud Foundry already supports multiple versions of the Node.js runtime. Developers select the desired runtime version using a parameter in their application’s package descriptor.

So, we just update package.json to include “4.0.0” and re-deploy our application?

Not yet.

There is an unresolved technical issue delaying the release of “official” Node.js v4 support for the platform. 😿

Can we add support ourselves?

Yes!

To do this, we need to explore how Cloud Foundry configures the runtime environment for applications.

Buildpacks

Rather than hardcoding supported runtimes and frameworks into the platform, Cloud Foundry borrowed the buildpack model from Heroku. Buildpacks are a set of scripts, run by the platform during deployment, to configure the runtime environment.

Users can set an explicit buildpack for an application, using the manifest, or let the platform decide. Buildpacks for common runtimes are pre-installed with the platform. Buildpacks set through the manifest can point to external URLs, allowing users to create new buildpacks supporting custom runtimes.

Each buildpack must contain the following files as executable scripts.

  • bin/detect - determine whether a buildpack is suitable for an application.
  • bin/compile - install and configure the runtime environment on the DEA.
  • bin/release - provide metadata with information on executing application.

Full details on existing buildpacks for the platform are available here.

Node.js is supported as an “official” buildpack by the platform. This will be the one we will modify to add support for the latest version of the runtime.

Node.js Buildpack

This is the Node.js buildpack for Cloud Foundry. Applications using this buildpack can select the version of Node.js to install using the engine parameter in the package descriptor.

Looking at the bin/compile script will show us how the Node.js runtime is installed during deployment.

This snippet handles accessing the Node.js version configured, using the node.engine parameter from package.json, before calling install_nodejs to install the correct runtime package.

install_bins() {
  local node_engine=$(read_json "$BUILD_DIR/package.json" ".engines.node")
  local npm_engine=$(read_json "$BUILD_DIR/package.json" ".engines.npm")

  echo "engines.node (package.json):  ${node_engine:-unspecified}"
  echo "engines.npm (package.json):   ${npm_engine:-unspecified (use default)}"
  echo ""

  warn_node_engine "$node_engine"
  install_nodejs "$node_engine" "$BUILD_DIR/.heroku/node"
  install_npm "$npm_engine" "$BUILD_DIR/.heroku/node"
  warn_old_npm
}

Searching through the buildpack for this function, it’s in the lib/binaries.sh file. Looking at the function code, it translates the version number into a URL pointing to an archive with the pre-compiled Node.js binary. This archive file is downloaded, extracted and installed into the runtime environment.

Translating Node.js version identifiers into archive URLs uses a special file in the buildpack, manifest.yml. This file maps every supported version to a pre-built binary location.

Looking at previous commits to the Node.js buildpack, adding support for additional versions of Node.js simply requires updating this file with the extra version identifier and archive URL.

Until the Cloud Foundry team updates the buildpack to support Node.js v4, they won’t provide an external archive containing the pre-built runtime environment.

Where can we find a suitable build of the Node.js binary?

Node.js Runtime Binaries

Cloud Foundry borrowed the buildpack concept from Heroku and still maintains backwards compatibility with their platform. Heroku buildpacks will work with Cloud Foundry applications. The Node.js buildpack for Cloud Foundry is actually still a fork of Heroku’s.

Looking back through the original buildpack source, this URL template is used to translate Node.js versions to archive URLs being built by Heroku.

http://s3pository.heroku.com/node/v$version/node-v$version-$os-$cpu.tar.gz

Combining the correct version identifier and platform parameters with this template gave the following location for a potential build of the Node.js v4 runtime.

http://s3pository.heroku.com/node/v4.0.0/node-v4.0.0-linux-x64.tar.gz

Running curl against the location successfully downloaded the Node.js v4 binary archive!

Custom v4 Buildpack

Forking the Cloud Foundry Node.js buildpack on Github, we can update the manifest.yml with the Node.js v4 identifier pointing to the Heroku runtime archive. This external Git repository will be used as the buildpack identifier in the application manfest.

Deploying with v4

Having updated our application manifest with the custom buildpack location and set the updated node version flag, re-deploying our application will start it running on Node.js v4.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[20:02:29 ~]$ cf app sample-demo-app
Showing health and status for app sample-demo-app in org james.thomas@uk.ibm.com / space dev as james.thomas@uk.ibm.com...
OK

requested state: started
instances: 1/1
usage: 256M x 1 instances
urls: sample-demo-app.mybluemix.net
last uploaded: Fri Sep 18 18:33:56 UTC 2015
stack: lucid64
buildpack: SDK for Node.js(TM) (node.js-4.0.0)

     state     since                    cpu    memory          disk        details
#0   running   2015-09-18 07:35:01 PM   0.0%   65.3M of 256M   59M of 1G
[20:03:13 ~]$

Looking at the logs from the deployment we can see the latest Node.js runtime has been downloaded and installed within our runtime environment.

Conclusion

Buildpacks are a brilliant feature of Cloud Foundry.

Understanding how buildpacks are structured and used by the platform means we can start customising existing buildpacks and even start creating our own.

If you want to run Node.js applications using v4 on Cloud Foundry today, you can use the following buildpack created using the instructions above.

Cloud Foundry is currently adding support for the version to the official buildpack, follow their progress here.

Comments