Deploying Node.js Applications with AWS OpsWorks — part 2

Alexander Springer
Product & Engineering at blogfoster
3 min readApr 29, 2016

--

In the last article I described how to build a simple cookbook for deploying Node.JS applications using Chef. Check it out if you want to recapitulate the steps as we will re-use them here.

In this post I want to describe how to group your Chef code using Custom Resources, which were introduced with Chef 12.5.

Custom Resources

Resources are the most essential part of the Chef DSL like git, package or template. Once upon a time ... to create your own resources you had to use HWRP (heavy weight resource providers). Then LWRP (light weight resource providers) were introduced to make your life easier. Now there's this new kid on the block called Custom Resources and again it should make things even more simple. I don't want to go into details, but what I noticed first is the ability to run recipes from within your Custom Resource is now as simple as include_recipe.

So why do we even need to define our own resources?

Coming back to our Cookbook, we defined five steps that are necessary for the deployment:

  1. install git
  2. install nodejs
  3. get the code through git/github
  4. install external node packages
  5. run the app

First of all, not all of these steps are necessary for each deployment. Secondly Chef Resources are easier to handle, than include_recipe syntax and thirdly they are easier to test, especially for users of your cookbook, who don't need to stub things that come from within you cookbook logic.

Ok, now we decided to build a Custom Resource for our deployment, how does it look like and how do we split it? As I mentioned before we might not want to run all steps for each deployment. This decision is more subjective and for our deployment I decided to not try to install git (1) or Node.js (2) on each run, whereas code updates (3), npm dependencies (4) and the app run environment (5) should be checked on each run. This doesn’t mean you cannot update Node.js later on, but I will come to this later.

Now we know how to split up functionality, we will create two Custom Resources, each of them must be one file located in the cookbook’s directory within a resources folder. The fist one will be named setup.rb and the other one deploy.rb. The filename plus it's cookbook will give the resource it's name, for simplicity we will assume this cookbook is called node-app. So afterwards we will have the two resources node_app_setup and node_app_deploy.

Enough talking, let’s write some code :)

So what did we do here. Most of the code is copied from the last blog post, but what changed is, that our Chef code is part of an action block and strings we hard-coded before are now defined as properties. Check out the Chef docs for Custom Resources to learn more details. This means we can now call the resources from outside and configure them for our needs, like this:

Doesn’t it look amazing?

As an user, you don’t need to care anymore what’s happening behind the scenes, but as the maintainer you can easily change things in the background without anyone telling! Also both functionalities can still be triggered on each deployment, but it’s simplier now to just run the node_app_deploy resource on each run and node_app_setup only on one intial run.

Chefspec Matcher

As a side note I want to mention, that with the created resources it’s now simpler for a user of your cookbook to run tests using chefspec, as the normal spec run does not step into resources. To make the life of your user's simpler, every resource should come with a chefspec matcher! It's not more than a matchers.rb file inside your cookbook within the libraries folder, that looks like this:

In any chefspec test it can now be used like:

Conclusion

So let’s review what we did: We encapsulated the logic for our deployment into two Custom Resources, node_app_setup and node_app_deploy to give the user the possibility to use our deployment cookbook for multiple different configurations and provided a matchers.rb file to simplify external testability.

In the next article we will see how to use our new resources with AWS OpsWorks and the data OpsWorks injects.

--

--