added the ability to limit memory consumption of code submissions executed using Docker
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
FactoryGirl.define do
|
||||
factory :coffee_script, class: ExecutionEnvironment do
|
||||
created_by_teacher
|
||||
default_memory_limit
|
||||
docker_image 'hklement/ubuntu-coffee:latest'
|
||||
association :file_type, factory: :dot_coffee
|
||||
help
|
||||
@@ -13,6 +14,7 @@ FactoryGirl.define do
|
||||
|
||||
factory :html, class: ExecutionEnvironment do
|
||||
created_by_teacher
|
||||
default_memory_limit
|
||||
docker_image 'hklement/ubuntu-html:latest'
|
||||
association :file_type, factory: :dot_html
|
||||
help
|
||||
@@ -27,6 +29,7 @@ FactoryGirl.define do
|
||||
|
||||
factory :java, class: ExecutionEnvironment do
|
||||
created_by_teacher
|
||||
default_memory_limit
|
||||
docker_image 'hklement/ubuntu-java:latest'
|
||||
association :file_type, factory: :dot_java
|
||||
help
|
||||
@@ -41,6 +44,7 @@ FactoryGirl.define do
|
||||
|
||||
factory :jruby, class: ExecutionEnvironment do
|
||||
created_by_teacher
|
||||
default_memory_limit
|
||||
docker_image 'hklement/ubuntu-jruby:latest'
|
||||
association :file_type, factory: :dot_rb
|
||||
help
|
||||
@@ -55,6 +59,7 @@ FactoryGirl.define do
|
||||
|
||||
factory :node_js, class: ExecutionEnvironment do
|
||||
created_by_teacher
|
||||
default_memory_limit
|
||||
docker_image 'hklement/ubuntu-node:latest'
|
||||
association :file_type, factory: :dot_js
|
||||
help
|
||||
@@ -67,6 +72,7 @@ FactoryGirl.define do
|
||||
|
||||
factory :python, class: ExecutionEnvironment do
|
||||
created_by_teacher
|
||||
default_memory_limit
|
||||
docker_image 'hklement/ubuntu-python:latest'
|
||||
association :file_type, factory: :dot_py
|
||||
help
|
||||
@@ -81,6 +87,7 @@ FactoryGirl.define do
|
||||
|
||||
factory :ruby, class: ExecutionEnvironment do
|
||||
created_by_teacher
|
||||
default_memory_limit
|
||||
docker_image 'hklement/ubuntu-ruby:latest'
|
||||
association :file_type, factory: :dot_rb
|
||||
help
|
||||
@@ -95,6 +102,7 @@ FactoryGirl.define do
|
||||
|
||||
factory :sinatra, class: ExecutionEnvironment do
|
||||
created_by_teacher
|
||||
default_memory_limit
|
||||
docker_image 'hklement/ubuntu-sinatra:latest'
|
||||
association :file_type, factory: :dot_rb
|
||||
exposed_ports '4567'
|
||||
@@ -110,6 +118,7 @@ FactoryGirl.define do
|
||||
|
||||
factory :sqlite, class: ExecutionEnvironment do
|
||||
created_by_teacher
|
||||
default_memory_limit
|
||||
docker_image 'hklement/ubuntu-sqlite:latest'
|
||||
association :file_type, factory: :dot_sql
|
||||
help
|
||||
@@ -122,6 +131,10 @@ FactoryGirl.define do
|
||||
testing_framework 'SqlResultSetComparatorAdapter'
|
||||
end
|
||||
|
||||
trait :default_memory_limit do
|
||||
memory_limit DockerClient::DEFAULT_MEMORY_LIMIT
|
||||
end
|
||||
|
||||
trait :help do
|
||||
help { Forgery(:lorem_ipsum).words(Forgery(:basic).number(at_least: 50, at_most: 100)) }
|
||||
end
|
||||
|
@@ -25,6 +25,34 @@ describe DockerClient, docker: true do
|
||||
end
|
||||
end
|
||||
|
||||
describe '.container_creation_options' do
|
||||
let(:container_creation_options) { described_class.container_creation_options(execution_environment) }
|
||||
|
||||
it 'specifies the Docker image' do
|
||||
expect(container_creation_options).to include('Image' => described_class.find_image_by_tag(execution_environment.docker_image).info['RepoTags'].first)
|
||||
end
|
||||
|
||||
it 'specifies the memory limit' do
|
||||
expect(container_creation_options).to include('Memory' => execution_environment.memory_limit.megabytes)
|
||||
end
|
||||
|
||||
it 'specifies to open the standard input stream once' do
|
||||
expect(container_creation_options).to include('OpenStdin' => true, 'StdinOnce' => true)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.container_start_options' do
|
||||
let(:container_start_options) { described_class.container_start_options(execution_environment, '') }
|
||||
|
||||
it 'specifies mapped directories' do
|
||||
expect(container_start_options).to include('Binds' => kind_of(Array))
|
||||
end
|
||||
|
||||
it 'specifies mapped ports' do
|
||||
expect(container_start_options).to include('PortBindings' => kind_of(Hash))
|
||||
end
|
||||
end
|
||||
|
||||
describe '.create_container' do
|
||||
let(:create_container) { described_class.create_container(execution_environment) }
|
||||
|
||||
@@ -39,25 +67,25 @@ describe DockerClient, docker: true do
|
||||
create_container
|
||||
end
|
||||
|
||||
it 'creates a container waiting for input' do
|
||||
expect(Docker::Container).to receive(:create).with('Image' => kind_of(String), 'OpenStdin' => true, 'StdinOnce' => true).and_call_original
|
||||
it 'creates a container' do
|
||||
expect(described_class).to receive(:container_creation_options).with(execution_environment).and_call_original
|
||||
expect(Docker::Container).to receive(:create).with(kind_of(Hash)).and_call_original
|
||||
create_container
|
||||
end
|
||||
|
||||
it 'starts the container' do
|
||||
expect_any_instance_of(Docker::Container).to receive(:start)
|
||||
expect(described_class).to receive(:container_start_options).with(execution_environment, kind_of(String)).and_call_original
|
||||
expect_any_instance_of(Docker::Container).to receive(:start).with(kind_of(Hash)).and_call_original
|
||||
create_container
|
||||
end
|
||||
|
||||
it 'configures mapped directories' do
|
||||
expect(described_class).to receive(:mapped_directories).and_call_original
|
||||
expect_any_instance_of(Docker::Container).to receive(:start).with(hash_including('Binds' => kind_of(Array)))
|
||||
create_container
|
||||
end
|
||||
|
||||
it 'configures mapped ports' do
|
||||
expect(described_class).to receive(:mapped_ports).with(execution_environment).and_call_original
|
||||
expect_any_instance_of(Docker::Container).to receive(:start).with(hash_including('PortBindings' => kind_of(Hash)))
|
||||
create_container
|
||||
end
|
||||
|
||||
|
@@ -13,6 +13,21 @@ describe ExecutionEnvironment do
|
||||
expect(execution_environment.errors[:docker_image]).to be_present
|
||||
end
|
||||
|
||||
it 'validates the minimum value of the memory limit' do
|
||||
execution_environment.update(memory_limit: DockerClient::MINIMUM_MEMORY_LIMIT / 2)
|
||||
expect(execution_environment.errors[:memory_limit]).to be_present
|
||||
end
|
||||
|
||||
it 'validates the numericality of the memory limit' do
|
||||
execution_environment.update(memory_limit: Math::PI)
|
||||
expect(execution_environment.errors[:memory_limit]).to be_present
|
||||
end
|
||||
|
||||
it 'validates the presence of a memory limit' do
|
||||
execution_environment.update(memory_limit: nil)
|
||||
expect(execution_environment.errors[:memory_limit]).to be_present
|
||||
end
|
||||
|
||||
it 'validates the presence of a name' do
|
||||
expect(execution_environment.errors[:name]).to be_present
|
||||
end
|
||||
|
Reference in New Issue
Block a user