If you’re using Capistrano to deploy code to web servers and Puppet to manage those servers, the DRY principle suggests that it may be a bad idea to hardcode your list of web servers in your Capistrano recipe — instead, you may want to dynamically fetch the list of web servers from Puppet prior to each deploy. Here’s one way of doing this.
Puppetmaster Configuration
First, you’ll need to turn on storeconfigs on your Puppetmaster. This allows Puppet to store all of its node information in a database (which enables us to access it for other purposes). Note that these instructions assume you have a MySQL database for your storeconfigs.
Next, create a script on your Puppetmaster called /usr/local/bin/list_nodes_by_class.rb. Fill in the database username, password, host and schema name used to access your storeconfigs in lines 4-7. Make sure the script is executable.
#!/usr/bin/ruby require 'mysql' MY_USER="yourdbuser" MY_PASS="yourdbpass" MY_HOST="yourdbserver" MY_DB="yourdbname" QUERY="select h.name from hosts h join resources r on h.id = r.host_id join resource_tags rt on r.id = rt.resource_id join puppet_tags pt on rt.puppet_tag_id = pt.id where pt.name = 'class' and r.restype = 'Class' and r.title = '#{ARGV[0].gsub("'","")}' order by h.name;" my = Mysql.new(MY_HOST, MY_USER, MY_PASS, MY_DB) res = my.query(QUERY) res.each_hash { |row| puts row['name'] }
Capistrano Recipe Modification
Note that I’ll assume here that your Capistrano recipe uses the “web” role to determine where to deploy code to, and that the Puppet class you use to designate web servers is “role_webserver” — you may need to change these.
First, add the following task and helper function to your recipe:
task :set_roles_from_puppet, :roles => :puppetmaster do get_nodes_by_puppet_class('role_webserver').each {|s| role :web, s} end def get_nodes_by_puppet_class(classname) hosts = [] run "/usr/local/bin/list_nodes_by_class.rb #{classname}", :pty => true do |ch, stream, out| out.split("\r\n").each { |host| hosts << host } end hosts end
Next, go to wherever you’re defining your roles. Add a role for your Puppetmaster:
role :puppetmaster, "puppetmaster.mynetwork.com"
set_roles_from_puppet
Now, when you do a Capistrano deploy, the destination servers should be dynamically retrieved from the Puppet database.
Leave a Reply