my application cookbooks define attribute like this which define node data in the node['sk_services']
namespace which is designed to be searchable by other nodes and give information (in this case information that an nginx proxy needs to know).
% cat attributes/default.rb
default['sk_services']['www.visibility-project.com']['normal_port'] = 80
default['sk_services']['www.visibility-project.com']['ssl_port'] = 443
default['sk_services']['www.visibility-project.com']['listen_port'] = 8080
default['sk_services']['www.visibility-project.com']['cache_dirs'] = [
'assets',
]
default['sk_services']['www.visibility-project.com']['cache_expires'] = '1w'
default['sk_services']['www.visibility-project.com']['alias'] = 'visibility'
I define a helper in a library which does the search on the current environment and gets all the sk_services
data on all the nodes. This information is then saved in the chef run_state
on the searching node.
class SKServices
extend Chef::DSL::DataQuery
def self.load_services(node)
return node.run_state[:sk_services_hash] if node.run_state.has_key?(:sk_services_hash)
node.save # hack in case we're search for ourselves as newly-provisioned node
sk_service_nodes = nil
tries = 50
loop do
Chef::Log.info("Searching for sk_service nodes...")
sk_service_nodes = search(:node, "sk_services:[* TO *] AND chef_environment:#{node.chef_environment}")
break unless sk_service_nodes.empty?
break unless ( tries -= 1 ) < 0
Chef::Log.info("Found zero nodes in #{node.chef_environment} environment, retrying (left: #{tries})")
sleep 2
end
# FIXME: partial search to return only node['sk_services'] and node['ipaddress']
sk_services_hash = {}
sk_service_nodes.each do |node_data|
node_data['sk_services'].each do |service, map|
hash = map.to_hash
hash['vhost'] = service
sk_services_hash[service] = hash
sk_services_hash[service]['upstreams'] ||= []
sk_services_hash[service]['upstreams'].push(node_data.ipaddress)
end
end
node.run_state[:sk_services_hash] = sk_services_hash
return sk_services_hash
end
end
Using this in recipe code on the nginx load balancer in order to setup VIPs it looks like this:
sk_services = SKServices.load_services(node)
sk_services.each do |site, hash|
# strip off domain name
short_name = hash['alias'] ? hash['alias'] : site.gsub(/\..*/, "")
hash['upstream_name'] = hash['vhost'].gsub(/[\.\-]/,'_')
template "/etc/nginx/sites-available/#{short_name}" do
source "site.erb"
mode "0644"
variables hash
if ::File.symlink?("/etc/nginx/sites-enabled/#{short_name}")
notifies :reload, resources(:service => "nginx")
end
end
nginx_site "#{short_name}" do
enable true
notifies :restart, resources(:service => "nginx")
end
end