Skip to content

Instantly share code, notes, and snippets.

@lamont-granquist
Last active January 11, 2020 22:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lamont-granquist/f6f5c633c732ed1522b0 to your computer and use it in GitHub Desktop.
Save lamont-granquist/f6f5c633c732ed1522b0 to your computer and use it in GitHub Desktop.

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment