class COMAP::App

Rack App

Public Class Methods

new( docker_host: 'http://127.0.0.1', docker_port: '2375', docker_ssl: '', metrics_path: '', network: '', services: [] ) click to toggle source

rubocop:disable Metrics/ParameterLists

# File lib/comap/app.rb, line 26
def initialize(
  docker_host: 'http://127.0.0.1',
  docker_port: '2375',
  docker_ssl: '',
  metrics_path: '',
  network: '',
  services: []
)
  raise ArgumentError, 'No services given' if services.empty?
  raise ArgumentError, 'No network given' if network.nil?

  ssl_hash = parse_ssl(docker_ssl)
  @client = DockerClient.new(docker_host, docker_port, ssl_hash)
  @network = network
  @services = services
  @metrics_path = metrics_path
  error_msg = 'Could not retrieve Docker API version from '\
    "#{docker_host}:#{docker_port} ssl:#{docker_ssl}"
  raise StandardError, error_msg if @client.call('version').nil?
end

Public Instance Methods

call(_env) click to toggle source

rubocop:enable Metrics/ParameterLists

# File lib/comap/app.rb, line 48
def call(_env)
  ['200', { 'Content-Type' => 'text/plain' }, [body]]
end

Private Instance Methods

add_label(line, service, slot) click to toggle source
# File lib/comap/app.rb, line 90
def add_label(line, service, slot)
  replace = %(service="#{service}",container="#{service}.#{slot}")
  if line[/{.*?\}/].nil?
    line.sub(/ /, "{#{replace}} ")
  else
    line.sub(/(\{(.*)\})/, "{\\2,#{replace}}")
  end
end
body() click to toggle source
# File lib/comap/app.rb, line 54
def body
  @services.map do |s|
    service, port = s.split(':')
    service_metrics(service, port)
  end.compact.map(&flat_hash).join
end
container_metrics(url, service, slot) click to toggle source
# File lib/comap/app.rb, line 70
def container_metrics(url, service, slot)
  data = scrape_metrics(url, service)
  return {} if data.nil?

  data.lines.each_with_object({}) do |value, hash|
    next hash[value] = nil if value.start_with?('#')

    (hash[hash.keys.last] ||= []) << add_label(value, service, slot)
  end.compact
end
containers(service) click to toggle source
# File lib/comap/app.rb, line 99
def containers(service)
  running_tasks(service).map do |task|
    {
      'slot' => task['Slot'] || task['NodeID'],
      'ip' => ip(task).split('/').first
    }
  end.uniq
end
flat_hash() click to toggle source
# File lib/comap/app.rb, line 144
def flat_hash
  proc do |item|
    item.map { |key, value| "#{key}#{value.join}" }
  end
end
ip(task) click to toggle source
# File lib/comap/app.rb, line 118
def ip(task)
  task['NetworksAttachments'].select do |h|
    h['Network']['Spec']['Name'] == @network
  end.first['Addresses'].first
end
merge_proc() click to toggle source
# File lib/comap/app.rb, line 138
def merge_proc
  proc do |old, new|
    old.merge(new) { |_, value1, value2| value1 + value2 }
  end
end
parse_ssl(docker_ssl) click to toggle source

helpers

# File lib/comap/app.rb, line 126
def parse_ssl(docker_ssl)
  docker_ssl.split(',').map do |opt|
    k, v = opt.split('=')
    if k.include?('cert')
      v = OpenSSL::X509::Certificate.new(File.read(v))
    elsif k.include? 'key'
      v = OpenSSL::PKey::RSA.new(File.read(v))
    end
    [k, v]
  end.to_h
end
running_tasks(service) click to toggle source
# File lib/comap/app.rb, line 108
def running_tasks(service)
  filters = {
    'service' => { service => true },
    'desired-state' => { 'running' => true }
  }
  @client.call('tasks', filters).keep_if do |task|
    task['Status']['State'] == 'running' if task.is_a?(Hash)
  end
end
scrape_metrics(url, service) click to toggle source
# File lib/comap/app.rb, line 81
def scrape_metrics(url, service)
  Faraday.get(url) do |req|
    req.options.timeout = 2
    req.options.open_timeout = 1
  end.body
rescue StandardError
  puts "Could not connect to #{service} endpoint #{url}"
end
service_metrics(service, port) click to toggle source
# File lib/comap/app.rb, line 61
def service_metrics(service, port)
  containers(service).sort_by { |c| c['slot'] }.map do |container|
    next puts "#{service} service not found" if container.empty?

    url = "http://#{container['ip']}:#{port}/#{@metrics_path}"
    container_metrics(url, service, container['slot'])
  end.reduce(&merge_proc)
end