class Coverband::Adapters::Service

Take Coverband data and store a merged coverage set to the Coverband service

NOTES:

Attributes

coverband_url[R]
hostname[R]
pid[R]
process_type[R]
runtime_env[R]
stats[R]

Public Class Methods

new(coverband_url, opts = {}) click to toggle source
Calls superclass method
# File lib/coverband-service-client.rb, line 45
def initialize(coverband_url, opts = {})
  super()
  @coverband_url = coverband_url
  @process_type = opts.fetch(:process_type) { $PROGRAM_NAME&.split('/')&.last || COVERBAND_PROCESS_TYPE }
  @hostname = opts.fetch(:hostname) { ENV["DYNO"] || Socket.gethostname.force_encoding('utf-8').encode }
  @hostname = @hostname.gsub("'",'').gsub("’",'')
  @runtime_env = opts.fetch(:runtime_env) { COVERBAND_ENV }
  @failed_coverage_reports = []
  initialize_stats
end

Public Instance Methods

api_key() click to toggle source
# File lib/coverband-service-client.rb, line 92
def api_key
  ENV['COVERBAND_API_KEY'] || Coverband.configuration.api_key
end
clear!() click to toggle source
# File lib/coverband-service-client.rb, line 79
def clear!
  # TBD
end
clear_file!(filename) click to toggle source
# File lib/coverband-service-client.rb, line 83
def clear_file!(filename)
  # TBD
end
coverage(local_type = nil, opts = {}) click to toggle source

Fetch coverband coverage via the API

# File lib/coverband-service-client.rb, line 99
def coverage(local_type = nil, opts = {})
  local_type ||= opts.key?(:override_type) ? opts[:override_type] : type
  env_filter = opts.key?(:env_filter) ? opts[:env_filter] : 'production'
  uri = URI("#{coverband_url}/api/coverage?type=#{local_type}&env_filter=#{env_filter}",)
  req = Net::HTTP::Get.new(uri, 'Content-Type' => 'application/json', 'Coverband-Token' => api_key)
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
    http.request(req)
  end
  coverage_data = JSON.parse(res.body)
  coverage_data
rescue StandardError => e
  logger&.error "Coverband: Error while retrieving coverage #{e}" if Coverband.configuration.verbose || COVERBAND_ENABLE_DEV_MODE
end
initialize_stats() click to toggle source
# File lib/coverband-service-client.rb, line 56
def initialize_stats
  return unless ENV['COVERBAND_STATS_KEY']
  return unless defined?(Dogapi::Client)

  @stats = Dogapi::Client.new(ENV['COVERBAND_STATS_KEY'])
  @app_name = defined?(Rails) ? Rails.application.class.module_parent.to_s : "unknown"
end
logger() click to toggle source
# File lib/coverband-service-client.rb, line 75
def logger
  Coverband.configuration.logger
end
raw_store() click to toggle source
# File lib/coverband-service-client.rb, line 145
def raw_store
  self
end
report_timing(timing) click to toggle source
# File lib/coverband-service-client.rb, line 64
def report_timing(timing)
  return unless @stats

  @stats.emit_point(
    'coverband.save.time',
    timing,
    host: hostname,
    device: "coverband_#{self.class.name.split("::").last}",
    options: {tags: [runtime_env]})
end
save_report(report) click to toggle source
# File lib/coverband-service-client.rb, line 113
def save_report(report)
  return if report.empty?

  # We set here vs initialize to avoid setting on the primary process vs child processes
  @pid ||= ::Process.pid

  # TODO: do we need dup
  # TODO: remove upstream timestamps, server will track first_seen
  Thread.new do
    data = expand_report(report.dup)
    full_package = {
      collection_type: 'coverage_delta',
      collection_data: {
        tags: {
          process_type: process_type,
          app_loading: type == Coverband::EAGER_TYPE,
          runtime_env: runtime_env,
          pid: pid,
          hostname: hostname,
        },
        file_coverage: data
      }
    }

    starting = Process.clock_gettime(Process::CLOCK_MONOTONIC) if @stats
    save_coverage(full_package)
    ending = Process.clock_gettime(Process::CLOCK_MONOTONIC) if @stats
    report_timing((ending - starting)) if @stats
    retry_failed_reports
  end&.join
end
size() click to toggle source

TODO: we should support nil to mean not supported

# File lib/coverband-service-client.rb, line 88
def size
  0
end

Private Instance Methods

add_retry_message(report_body) click to toggle source
# File lib/coverband-service-client.rb, line 164
def add_retry_message(report_body)
  if @failed_coverage_reports.length > 5
    logger&.info "Coverband: The errored reporting queue has reached 5. Subsequent reports will not be transmitted"
  else
    @failed_coverage_reports << report_body
  end
end
retry_failed_reports() click to toggle source
# File lib/coverband-service-client.rb, line 151
def retry_failed_reports
  retries = []
  @failed_coverage_reports.any? do
    report_body = @failed_coverage_reports.pop
    send_report_body(report_body)
  rescue StandardError
    retries << report_body
  end
  retries.each do |report_body|
    add_retry_message(report_body)
  end
end
save_coverage(data) click to toggle source
# File lib/coverband-service-client.rb, line 172
def save_coverage(data)
  if api_key.nil?
    puts "Coverband: Error: no Coverband API key was found!"
    return
  end

  coverage_body = { remote_uuid: SecureRandom.uuid, data: data }.to_json
  send_report_body(coverage_body)
rescue StandardError => e
  add_retry_message(coverage_body)
  logger&.info "Coverband: Error while saving coverage #{e}" if Coverband.configuration.verbose || COVERBAND_ENABLE_DEV_MODE
end
send_report_body(coverage_body) click to toggle source
# File lib/coverband-service-client.rb, line 185
def send_report_body(coverage_body)
  uri = URI("#{coverband_url}/api/collector")
  req = ::Net::HTTP::Post.new(uri,
                            'Content-Type' => 'application/json',
                            'Coverband-Token' => api_key)
  req.body = coverage_body
  logger&.info "Coverband: saving (#{uri}) #{req.body}" if Coverband.configuration.verbose
  res = ::Net::HTTP.start(
    uri.hostname,
    uri.port,
    open_timeout: COVERBAND_TIMEOUT,
    read_timeout: COVERBAND_TIMEOUT,
    ssl_timeout: COVERBAND_TIMEOUT,
    use_ssl: uri.scheme == 'https'
    ) do |http|
    http.request(req)
  end
  if res.code.to_i >= 500
    add_retry_message(coverage_body)
  end
end