class CC::Analyzer::Engine

Running specifically an Engine container

Input:

- name
- metadata
  - image
  - command (optional)
- config (becomes /config.json)
- label
- io (to write filtered, validated output)

Output:

- Container::Result

Constants

Error

Attributes

error[RW]
metadata[R]
name[R]

Public Class Methods

new(name, metadata, config, label) click to toggle source
# File lib/cc/analyzer/engine.rb, line 23
def initialize(name, metadata, config, label)
  @name = name
  @metadata = metadata
  @config = config
  @label = label.to_s
  @error = nil
end

Public Instance Methods

run(io) click to toggle source
# File lib/cc/analyzer/engine.rb, line 31
def run(io)
  write_config_file

  container = Container.new(
    image: metadata.fetch("image"),
    command: metadata["command"],
    name: container_name,
  )

  container.on_output("\0") do |output|
    handle_output(container, io, output)
  end

  container.run(container_options).tap do |result|
    result.merge_from_exception(error) if error.present?
  end
ensure
  delete_config_file
end

Private Instance Methods

code() click to toggle source
# File lib/cc/analyzer/engine.rb, line 108
def code
  @code ||= MountedPath.code
end
config_file() click to toggle source
# File lib/cc/analyzer/engine.rb, line 112
def config_file
  @config_file ||= MountedPath.tmp.join(SecureRandom.uuid)
end
container_name() click to toggle source
# File lib/cc/analyzer/engine.rb, line 94
def container_name
  @container_name ||= "cc-engines-#{qualified_name.tr(":", "-")}-#{SecureRandom.uuid}"
end
container_options() click to toggle source
# File lib/cc/analyzer/engine.rb, line 76
def container_options
  options = [
    "--cap-drop", "all",
    "--label", "com.codeclimate.label=#{@label}",
    "--log-driver", "none",
    "--memory-swap", "-1",
    "--net", "none",
    "--rm",
    "--volume", "#{code.host_path}:/code:ro",
    "--volume", "#{config_file.host_path}:/config.json:ro",
    "--user", "9000:9000"
  ]
  if (memory = metadata["memory"]).present?
    options.concat(["--memory", memory.to_s])
  end
  options
end
delete_config_file() click to toggle source
# File lib/cc/analyzer/engine.rb, line 104
def delete_config_file
  config_file.delete if config_file.file?
end
handle_output(container, io, raw_output) click to toggle source
# File lib/cc/analyzer/engine.rb, line 56
def handle_output(container, io, raw_output)
  output = EngineOutput.new(name, raw_output)

  return if output_filter.filter?(output)

  unless output.valid?
    self.error = Error.new("engine produced invalid output: #{output.error}")
    container.stop("output invalid")
  end

  unless io.write(output_overrider.apply(output).to_json)
    self.error = Error.new("#{io.class}#write returned false, indicating an error")
    container.stop("output error")
  end
end
output_filter() click to toggle source
# File lib/cc/analyzer/engine.rb, line 116
def output_filter
  @output_filter ||= EngineOutputFilter.new(@config)
end
output_overrider() click to toggle source
# File lib/cc/analyzer/engine.rb, line 120
def output_overrider
  @output_overrider ||= EngineOutputOverrider.new(@config)
end
qualified_name() click to toggle source
# File lib/cc/analyzer/engine.rb, line 72
def qualified_name
  "#{name}:#{@config.fetch("channel", "stable")}"
end
write_config_file() click to toggle source
# File lib/cc/analyzer/engine.rb, line 98
def write_config_file
  @config["debug"] = ENV["CODECLIMATE_DEBUG"]
  Analyzer.logger.debug "/config.json content: #{@config.inspect}"
  config_file.write(@config.to_json)
end