Chap
chef + capistrano = chap: deploy your app with either chef or capistrano. This was written to solve the issue between having 2 deployment systems that are very similar but not exactly the same. With chap you can deploy to a single server by running one command:
$ chap deploy
The same command is called whether you're using chef or capistrano for deployment. The chap deploy command does the heavy lifting and manages the deploy instead of capistrano or chef.
Requirements
$ gem install chap
Setup
Chap requires 3 configuration files: chap.yml, chap.json and node.json.
- chap.json: contains capistrano-like configuration settings.
- node.json: is intended to be the same file that chef solo uses and contains instance specific information, like node[:instance_role].
- chap.yml: The paths of the chap.json and node.json are configured in this file.
Here are examples of the starter setup files that you can generate via:
$ chap setup -o /etc/chef
$ cat /etc/chef/chap.yml chap: /etc/chef/chap.json node: /etc/chef/node.json $ cat /etc/chef/chap.json { "repo": "git@github.com:tongueroo/chapdemo.git", "branch": "master", "application": "chapdemo", "deploy_to": "/data/chapdemo", "strategy": "checkout", "keep": 5, "user": "deploy", "group": "deploy" } $ cat /etc/chef/node.json { "environment": "staging", "application": "chapdemo", "instance_role": "app" }
Usage
The chap command is meant to be executed on the server which you want to deploy the code.
Deploy sequence
The deploy sequence is based on the sequence from the capistrano and chef deploy resource provider.
- Download code to [deploy_to]/releases/[timestamp]
- Run chap/deploy hook
- Symlink [deploy_to]/releases/[timestamp] to [deploy_to]/current
- Run chap/restart hook
- Clean up old releases
On the server:
$ chap deploy
From capistrano, on local or deploy box:
$ cap deploy # cap recipe calls "chap deploy"
Example capistrano deploy
namespace :deploy do
task :default do
run "chap deploy"
end
Chef Chap LWRP:
# chef LWRP creates chap.yml and chap.json setup files and calls "chap deploy" chap_deploy "chapdemo" do repo "git@github.com:tongueroo/chapdemo.git" revision "master" end
Chap loads up information from node.json because it needs the information for hooks, which tend to work differently for different server roles. For example, the chap/restart hook below will run "touch tmp/restart.txt" for an app role and will run "rvmsudo bluepill restart resque" for a resque role. Example:
$ cat chap/restart #!/usr/bin/env ruby if node[:instance_role] == 'app' run "cd #{current_path} && touch tmp/restart.txt" elsif node[:instance_role] == 'resque' run "rvmsudo bluepill restart resque" end
Deploy Hooks
Define your deploy hooks in the chap folder of the project. There are 2 deploy hooks.
- chap/deploy - runs after the code has been deployed but not yet symlinked
- chap/restart - runs after the code has been symlinked and app needs to be restarted
Deploy hooks get evaluated within the context of a chap deploy run and have some special variables and methods:
Special variables:
- node - contains data from /etc/chef/node.json. Avaiable as mash.
- chap - contains data from /etc/chef/chap.json and some special variables added by chap. Avaiable as mash. Special variables: release_path, current_path, shared_path, cached_path, latest_release. The special variables are also available directly as methods.
Special methods:
- run - output the command to be ran and runs command.
- log - log messages to [shared_path]/chap/chap.log.
- symlink_configs - useful as a chap/deploy hook. Symlinks any config files in [shared_path]/config/* over to [release_path]/config.
- with - used to prepend all commands within a block with another command. A example is provided below.
with example:
with "cd #{release_path} && RAILS_ENV=#{node[:environment]} " do run "rake do:something1" run "rake do:something2" end
is the same as:
run "cd #{release_path} && RAILS_ENV=#{node[:environment]} rake do:something1" run "cd #{release_path} && RAILS_ENV=#{node[:environment]} rake do:something2"
Test deploy hooks
When a chap hook fails, you might want to quicky test it on the server without having commit new code and running a full deploy. You can edit the chap/* hooks on the spot and test them via:
$ cap hook deploy $ cap hook restart
This will test the hooks on the latest timestamp release at [deploy_to]/releases/[timestamp].
Syncing restart phase
Some apps require that all the code be available on all the servers before a restart should happen on any of the servers. For example, if you're serving assets on the same server as your app code, you want to make sure that all the assets have been download on all servers before any of the servers start serving the new assets. To sync the retart phase you have to break out the capistano recipe so that it calls 2 chap command:
task :chap do
run "chap deploy -q --stop-at-symlink"
run "chap deploy -q --cont-at-symlink"
end