Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save bdcravens/83ea68e26edbaa32ec12 to your computer and use it in GitHub Desktop.
Save bdcravens/83ea68e26edbaa32ec12 to your computer and use it in GitHub Desktop.
Implementing a /healthcheck endpoint in Slack to read out consul status in a human-readable fashion
class SlackController < ApplicationController
skip_before_action :verify_authenticity_token
@@slack_security_tokens =
["copy-paste-the-token-you-get-from-Slack-when-configuring-the-integration-here"]
before_filter :bounce_access_not_from_slack
def healthcheck
services = SystemStatus.list_services
if services.blank?
render json: {
response_type: "in_channel",
text: "PlayerUI couldn't connect successfully to consul cluster. BAD NEWS."
}
else
arguments = params[:text]
node_filter = nil
if arguments.present? && services.include?(arguments.split(" ")[0])
# /healthcheck gm <-- Filters results down to only gm nodes
services = [arguments.split(" ")[0].strip]
elsif arguments.present? && (arguments.split(" ")[0].present?)
# /healthcheck 172.31.16.60 <-- Filters results down to only this IP.
node_filter = arguments.split(" ")[0].strip
end
response = []
services.each do |service|
status = SystemStatus.service_up?(service)
status_string = status ? "[UP]" : "[*DOWN*]"
nodes = SystemStatus.list_service_nodes(service)
node_details = []
nodes.each do |node|
tags = (node[:tags].length < 10) ? node[:tags].join("|") : (node[:tags][0..9].join("|") + "...")
detail = "#{node[:address]}:#{node[:port]} (#{tags})"
detail = "~#{detail}~" unless SystemStatus.service_up?(service, node[:tags].first)
node_details << detail if node_filter.blank? || (node_filter == node[:address])
end
response << [service, status_string, node_details].join(" ") if node_filter.blank? || node_details.present?
end
render json: {
response_type: "in_channel",
text: response.join("\n")
}
end
private
def bounce_access_not_from_slack
unless Rails.env.development? || @@slack_security_tokens.include?(params[:token])
raise "Not authorized: token sent doesn't match pre-configured Slack security token."
end
end
end
require 'resolv'
class SystemStatus
def self.list_services
services = JSON.parse(RestClient.get("http://localhost:8500/v1/catalog/services")) rescue nil
services ? services.keys : nil
end
def self.list_service_nodes(service)
nodes = JSON.parse(RestClient.get("http://localhost:8500/v1/catalog/service/#{service}")) rescue []
nodes.map do |node|
node[:address] = node["ServiceAddress"].present? ? node["ServiceAddress"] : node["Address"]
node[:port] = node["ServicePort"] || (node["Port"] || "0").to_i
node[:tags] = node["ServiceTags"] || []
node[:node] = node["Node"]
end
nodes
end
def self.service_up?(service, tag = nil)
if !tag
(!!dns.getaddress("#{service}.service.consul")) rescue false
else
(!!dns.getaddress("#{tag}.#{service}.service.consul")) rescue false
end
end
def self.node_up?(node)
(!!dns.getaddress("#{node}.node.consul")) rescue false
end
def self.service_statuses(bust_cache = false)
t = bust_cache ? nil : Time.now.to_i
Rails.cache.fetch("SystemStatus.service_statuses(#{t})", :expires_in => 1.minute) do
services = list_services
services.inject({}) {|acc, service| acc[service] = service_up?(service) ? "up" : "down"; acc}
end
end
def self.dns
Resolv::DNS.new(nameserver: ["127.0.0.1"])
end
end
MIT license, same as Rails. Feel free to hack the heck out of this.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment