Skip to content
This repository was archived by the owner on Sep 25, 2019. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/coursemology/evaluator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module Coursemology::Evaluator
extend ActiveSupport::Autoload

autoload :Client
autoload :DockerContainer
autoload :CLI
autoload :Models
autoload :Services
Expand Down
31 changes: 31 additions & 0 deletions lib/coursemology/evaluator/docker_container.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class Coursemology::Evaluator::DockerContainer < Docker::Container
class << self
def create(image, argv: nil)
pull_image(image)

ActiveSupport::Notifications.instrument('create.docker.evaluator.coursemology',
image: image) do |payload|
options = { 'Image' => image }
options['Cmd'] = argv if argv && !argv.empty?

payload[:container] = super(options)
end
end

private

def pull_image(image)
ActiveSupport::Notifications.instrument('pull.docker.evaluator.coursemology',
image: image) do
Docker::Image.create('fromImage' => image)
end
end
end

def delete
ActiveSupport::Notifications.instrument('destroy.docker.evaluator.coursemology',
container: id) do
super
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ class Coursemology::Evaluator::Services::EvaluateProgrammingPackageService
# The path to where the test report will be at.
REPORT_PATH = File.join(PACKAGE_PATH, 'report.xml')

# The ratio to multiply the memory limits from our evaluation to the container by.
MEMORY_LIMIT_RATIO = 1.megabyte / 1.kilobyte

# Executes the given package in a container.
#
# @param [Coursemology::Evaluator::Models::ProgrammingEvaluation] evaluation The evaluation
Expand Down Expand Up @@ -43,15 +46,15 @@ def execute

def create_container(image)
image_identifier = "coursemology/evaluator-image-#{image}"
ActiveSupport::Notifications.instrument('pull.docker.evaluator.coursemology',
image: image_identifier) do
Docker::Image.create('fromImage' => image_identifier)
end
Coursemology::Evaluator::DockerContainer.create(image_identifier, argv: container_arguments)
end

ActiveSupport::Notifications.instrument('create.docker.evaluator.coursemology',
image: image_identifier) do |payload|
payload[:container] = Docker::Container.create('Image' => image_identifier)
end
def container_arguments
result = []
result.push("-c#{@evaluation.time_limit}") if @evaluation.time_limit
result.push("-m#{@evaluation.memory_limit * MEMORY_LIMIT_RATIO}") if @evaluation.memory_limit

result
end

# Copies the contents of the package to the container.
Expand Down Expand Up @@ -156,9 +159,6 @@ def extract_test_report_archive(container)
end

def destroy_container(container)
ActiveSupport::Notifications.instrument('destroy.docker.evaluator.coursemology',
container: container.id) do
container.delete
end
container.delete
end
end
20 changes: 20 additions & 0 deletions spec/coursemology/evaluator/docker_container_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
RSpec.describe Coursemology::Evaluator::DockerContainer do
let(:image) { 'coursemology/evaluator-image-python:2.7' }
subject { Coursemology::Evaluator::DockerContainer.create(image) }

describe '.create' do
it 'instruments the pull' do
expect { subject }.to instrument_notification('pull.docker.evaluator.coursemology')
end

it 'instruments the creation' do
expect { subject }.to instrument_notification('create.docker.evaluator.coursemology')
end
end

describe '#delete' do
it 'instruments the destruction' do
expect { subject.delete }.to instrument_notification('destroy.docker.evaluator.coursemology')
end
end
end
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
require 'spec_helper'

RSpec.describe Coursemology::Evaluator::Services::EvaluateProgrammingPackageService do
let(:package) { build(:programming_evaluation) }
let(:package) { build(:programming_evaluation, *package_params) }
let(:package_params) { nil }
let(:image) { 'python:2.7' }
subject { Coursemology::Evaluator::Services::EvaluateProgrammingPackageService.new(package) }

describe '.execute' do
Expand All @@ -12,27 +14,26 @@
end

describe '#create_container' do
let(:image) { 'python:2.7' }
let(:container) { subject.send(:create_container, image) }

it 'prefixes the image with coursemology/evaluator-image' do
expect(Docker::Image).to \
receive(:create).with('fromImage' => "coursemology/evaluator-image-#{image}").
and_call_original
expect(Docker::Container).to \
receive(:create).with('Image' => "coursemology/evaluator-image-#{image}")
expect(Coursemology::Evaluator::DockerContainer).to \
receive(:create).with("coursemology/evaluator-image-#{image}",
hash_including(argv: []))

container
end

it 'instruments the pull' do
expect { subject.send(:create_container, image) }.to \
instrument_notification('pull.docker.evaluator.coursemology')
end
context 'when the evaluation has resource limits' do
let(:package_params) { [time_limit: 5, memory_limit: 16] }

it 'specifies them to the container' do
expect(Coursemology::Evaluator::DockerContainer).to \
receive(:create).with("coursemology/evaluator-image-#{image}",
hash_including(argv: ['-c5', '-m16384']))

it 'instruments the creation' do
expect { subject.send(:create_container, image) }.to \
instrument_notification('create.docker.evaluator.coursemology')
subject.send(:create_container, image)
end
end
end

Expand Down Expand Up @@ -62,7 +63,6 @@
end

describe '#execute_package' do
let(:image) { 'python:2.7' }
let(:container) { subject.send(:create_container, image) }
after { subject.send(:destroy_container, container) }

Expand All @@ -83,7 +83,6 @@ def evaluate_result
end

describe '#execute_package_wait' do
let(:image) { 'python:2.7' }
let(:container) { subject.send(:create_container, image) }
after { subject.send(:destroy_container, container) }

Expand All @@ -101,7 +100,6 @@ def evaluate_result
end

describe '#extract_result' do
let(:image) { 'python:2.7' }
let(:container) do
subject.send(:create_container, image).tap do |container|
subject.send(:execute_package, container)
Expand All @@ -117,7 +115,6 @@ def evaluate_result
end

describe '#extract_test_report' do
let(:image) { 'python:2.7' }
let(:report_path) { File.join(__dir__, '../../../fixtures/sample_report.xml') }
let(:report_contents) { File.read(report_path) }
let(:container) { subject.send(:create_container, image) }
Expand All @@ -144,14 +141,4 @@ def copy_dummy_report
end
end
end

describe '#destroy_container' do
let(:image) { 'python:2.7' }
let(:container) { subject.send(:create_container, image) }

it 'instruments the destruction' do
expect { subject.send(:destroy_container, container) }.to \
instrument_notification('destroy.docker.evaluator.coursemology')
end
end
end
4 changes: 2 additions & 2 deletions spec/factories/programming_evaluations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ def self.define_package(evaluation)
factory :programming_evaluation, class: Coursemology::Evaluator::Models::ProgrammingEvaluation do
id 1
language Coursemology::Polyglot::Language::Python::Python2Point7.name
memory_limit 32
time_limit 5
memory_limit nil
time_limit nil

after(:stub) do |evaluation|
evaluation.define_singleton_method(:save) {}
Expand Down