diff --git a/app/controllers/concerns/common_behavior.rb b/app/controllers/concerns/common_behavior.rb
index 4c36cf63..8d63246d 100644
--- a/app/controllers/concerns/common_behavior.rb
+++ b/app/controllers/concerns/common_behavior.rb
@@ -5,10 +5,13 @@ module CommonBehavior
@object = options[:object]
respond_to do |format|
if @object.save
- yield if block_given?
+ notice = t('shared.object_created', model: @object.class.model_name.human)
+ if block_given?
+ result = yield
+ notice = result if result.present?
+ end
path = options[:path].try(:call) || @object
- respond_with_valid_object(format, notice: t('shared.object_created', model: @object.class.model_name.human),
-path: path, status: :created)
+ respond_with_valid_object(format, notice: notice, path: path, status: :created)
else
respond_with_invalid_object(format, template: :new)
end
@@ -42,9 +45,13 @@ path: path, status: :created)
@object = options[:object]
respond_to do |format|
if @object.update(options[:params])
+ notice = t('shared.object_updated', model: @object.class.model_name.human)
+ if block_given?
+ result = yield
+ notice = result if result.present?
+ end
path = options[:path] || @object
- respond_with_valid_object(format, notice: t('shared.object_updated', model: @object.class.model_name.human),
-path: path, status: :ok)
+ respond_with_valid_object(format, notice: notice, path: path, status: :ok)
else
respond_with_invalid_object(format, template: :edit)
end
diff --git a/app/controllers/execution_environments_controller.rb b/app/controllers/execution_environments_controller.rb
index 4449fa41..95132b09 100644
--- a/app/controllers/execution_environments_controller.rb
+++ b/app/controllers/execution_environments_controller.rb
@@ -3,6 +3,8 @@
class ExecutionEnvironmentsController < ApplicationController
include CommonBehavior
+ RUNNER_MANAGEMENT_PRESENT = CodeOcean::Config.new(:code_ocean).read[:runner_management].present?
+
before_action :set_docker_images, only: %i[create edit new update]
before_action :set_execution_environment, only: MEMBER_ACTIONS + %i[execute_command shell statistics]
before_action :set_testing_framework_adapters, only: %i[create edit new update]
@@ -15,7 +17,9 @@ class ExecutionEnvironmentsController < ApplicationController
def create
@execution_environment = ExecutionEnvironment.new(execution_environment_params)
authorize!
- create_and_respond(object: @execution_environment)
+ create_and_respond(object: @execution_environment) do
+ copy_execution_environment_to_poseidon
+ end
end
def destroy
@@ -105,7 +109,7 @@ class ExecutionEnvironmentsController < ApplicationController
def execution_environment_params
if params[:execution_environment].present?
- params[:execution_environment].permit(:docker_image, :exposed_ports, :editor_mode, :file_extension, :file_type_id, :help, :indent_size, :memory_limit, :name, :network_enabled, :permitted_execution_time, :pool_size, :run_command, :test_command, :testing_framework).merge(
+ params[:execution_environment].permit(:docker_image, :exposed_ports, :editor_mode, :file_extension, :file_type_id, :help, :indent_size, :memory_limit, :cpu_limit, :name, :network_enabled, :permitted_execution_time, :pool_size, :run_command, :test_command, :testing_framework).merge(
user_id: current_user.id, user_type: current_user.class.name
)
end
@@ -155,6 +159,15 @@ class ExecutionEnvironmentsController < ApplicationController
end
def update
- update_and_respond(object: @execution_environment, params: execution_environment_params)
+ update_and_respond(object: @execution_environment, params: execution_environment_params) do
+ copy_execution_environment_to_poseidon
+ end
end
+
+ def copy_execution_environment_to_poseidon
+ unless RUNNER_MANAGEMENT_PRESENT && @execution_environment.copy_to_poseidon
+ t('execution_environments.form.errors.not_synced_to_poseidon')
+ end
+ end
+ private :copy_execution_environment_to_poseidon
end
diff --git a/app/models/execution_environment.rb b/app/models/execution_environment.rb
index e4d39a93..3db35c35 100644
--- a/app/models/execution_environment.rb
+++ b/app/models/execution_environment.rb
@@ -7,6 +7,10 @@ class ExecutionEnvironment < ApplicationRecord
include DefaultValues
VALIDATION_COMMAND = 'whoami'
+ RUNNER_MANAGEMENT_PRESENT = CodeOcean::Config.new(:code_ocean).read[:runner_management].present?
+ BASE_URL = CodeOcean::Config.new(:code_ocean).read[:runner_management][:url] if RUNNER_MANAGEMENT_PRESENT
+ HEADERS = {'Content-Type' => 'application/json'}.freeze
+ DEFAULT_CPU_LIMIT = 20
after_initialize :set_default_values
@@ -26,6 +30,8 @@ class ExecutionEnvironment < ApplicationRecord
validates :permitted_execution_time, numericality: {only_integer: true}, presence: true
validates :pool_size, numericality: {only_integer: true}, presence: true
validates :run_command, presence: true
+ validates :cpu_limit, presence: true, numericality: {greater_than: 0, only_integer: true}
+ validates :exposed_ports, format: {with: /\A(([[:digit:]]{1,5},)*([[:digit:]]{1,5}))?\z/}
def set_default_values
set_default_values_if_present(permitted_execution_time: 60, pool_size: 0)
@@ -36,6 +42,33 @@ class ExecutionEnvironment < ApplicationRecord
name
end
+ def copy_to_poseidon
+ return false unless RUNNER_MANAGEMENT_PRESENT
+
+ url = "#{BASE_URL}/execution-environments/#{id}"
+ response = Faraday.put(url, to_json, HEADERS)
+ return true if [201, 204].include? response.status
+
+ Rails.logger.warn("Could not create execution environment in Poseidon, got response: #{response.as_json}")
+ false
+ end
+
+ def to_json(*_args)
+ {
+ id: id,
+ image: docker_image,
+ prewarmingPoolSize: pool_size,
+ cpuLimit: cpu_limit,
+ memoryLimit: memory_limit,
+ networkAccess: network_enabled,
+ exposedPorts: exposed_ports_list,
+ }.to_json
+ end
+
+ def exposed_ports_list
+ (exposed_ports || '').split(',').map(&:to_i)
+ end
+
def valid_test_setup?
if test_command? ^ testing_framework?
errors.add(:test_command,
diff --git a/app/views/execution_environments/_form.html.slim b/app/views/execution_environments/_form.html.slim
index 13ee252e..9447d1c6 100644
--- a/app/views/execution_environments/_form.html.slim
+++ b/app/views/execution_environments/_form.html.slim
@@ -15,11 +15,14 @@
.help-block.form-text == t('.hints.docker_image')
.form-group
= f.label(:exposed_ports)
- = f.text_field(:exposed_ports, class: 'form-control', placeholder: '3000, 4000')
+ = f.text_field(:exposed_ports, class: 'form-control', placeholder: '3000,4000', pattern: '^((\d{1,5},)*(\d{1,5}))?$')
.help-block.form-text == t('.hints.exposed_ports')
.form-group
= f.label(:memory_limit)
= f.number_field(:memory_limit, class: 'form-control', min: DockerClient::MINIMUM_MEMORY_LIMIT, value: f.object.memory_limit || DockerClient::DEFAULT_MEMORY_LIMIT)
+ .form-group
+ = f.label(:cpu_limit)
+ = f.number_field(:cpu_limit, class: 'form-control', min: 1, step: 1, value: ExecutionEnvironment::DEFAULT_CPU_LIMIT)
.form-check.mb-3
label.form-check-label
= f.check_box(:network_enabled, class: 'form-check-input')
diff --git a/app/views/execution_environments/show.html.slim b/app/views/execution_environments/show.html.slim
index 21133a71..6d9fb32e 100644
--- a/app/views/execution_environments/show.html.slim
+++ b/app/views/execution_environments/show.html.slim
@@ -5,7 +5,7 @@ h1
= row(label: 'execution_environment.name', value: @execution_environment.name)
= row(label: 'execution_environment.user', value: link_to_if(policy(@execution_environment.author).show?, @execution_environment.author, @execution_environment.author))
= row(label: 'execution_environment.file_type', value: @execution_environment.file_type.present? ? link_to(@execution_environment.file_type, @execution_environment.file_type) : nil)
-- [:docker_image, :exposed_ports, :memory_limit, :network_enabled, :permitted_execution_time, :pool_size].each do |attribute|
+- [:docker_image, :exposed_ports, :memory_limit, :cpu_limit, :network_enabled, :permitted_execution_time, :pool_size].each do |attribute|
= row(label: "execution_environment.#{attribute}", value: @execution_environment.send(attribute))
- [:run_command, :test_command].each do |attribute|
= row(label: "execution_environment.#{attribute}") do
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 29cf2eb4..344f1d58 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -14,6 +14,7 @@ de:
file_type_id: Standard-Dateityp
help: Hilfetext
memory_limit: Speicher-Limit (in MB)
+ cpu_limit: CPU-Limit (in MHz)
network_enabled: Netzwerkzugriff
name: Name
permitted_execution_time: Erlaubte Ausführungszeit (in Sekunden)
@@ -281,7 +282,9 @@ de:
hints:
command: filename wird automatisch durch den richtigen Dateinamen ersetzt.
docker_image: 'Wählen Sie ein Docker-Image aus der Liste oder fügen Sie ein neues hinzu, welches über DockerHub verfügbar ist.'
- exposed_ports: Während der Ausführung sind diese Ports für den Nutzer zugänglich.
+ exposed_ports: Während der Ausführung sind diese Ports für den Nutzer zugänglich. Die Portnummern müssen mit Komma, aber ohne Leerzeichen voneinander getrennt sein.
+ errors:
+ not_synced_to_poseidon: Die Ausführungsumgebung wurde erstellt, aber aufgrund eines Fehlers nicht zu Poseidon synchronisiert.
index:
shell: Shell
shell:
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 41c37dfc..183a1186 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -14,6 +14,7 @@ en:
file_type_id: Default File Type
help: Help Text
memory_limit: Memory Limit (in MB)
+ cpu_limit: CPU Limit (in MHz)
name: Name
network_enabled: Network Enabled
permitted_execution_time: Permitted Execution Time (in Seconds)
@@ -281,7 +282,9 @@ en:
hints:
command: filename is automatically replaced with the correct filename.
docker_image: Pick a Docker image listed above or add a new one which is available via DockerHub.
- exposed_ports: During code execution these ports are accessible for the user.
+ exposed_ports: During code execution these ports are accessible for the user. Port numbers must be separated by a comma but no space.
+ errors:
+ not_synced_to_poseidon: The ExecutionEnvironment was created but not synced to Poseidon due to an error.
index:
shell: Shell
shell:
diff --git a/db/migrate/20210601095654_add_cpu_limit_to_execution_environment.rb b/db/migrate/20210601095654_add_cpu_limit_to_execution_environment.rb
new file mode 100644
index 00000000..c74aa69a
--- /dev/null
+++ b/db/migrate/20210601095654_add_cpu_limit_to_execution_environment.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddCpuLimitToExecutionEnvironment < ActiveRecord::Migration[6.1]
+ def change
+ add_column :execution_environments, :cpu_limit, :integer, default: 20
+ end
+end
diff --git a/db/migrate/20210602071834_clean_exposed_ports_in_execution_environment.rb b/db/migrate/20210602071834_clean_exposed_ports_in_execution_environment.rb
new file mode 100644
index 00000000..32db9950
--- /dev/null
+++ b/db/migrate/20210602071834_clean_exposed_ports_in_execution_environment.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class CleanExposedPortsInExecutionEnvironment < ActiveRecord::Migration[6.1]
+ def change
+ ExecutionEnvironment.all.each do |execution_environment|
+ continue if execution_environment.exposed_ports.nil?
+
+ cleaned = execution_environment.exposed_ports.gsub(/[[:space:]]/, '')
+ list = cleaned.split(',').map(&:to_i).uniq
+ if list.empty?
+ execution_environment.update(exposed_ports: nil)
+ else
+ execution_environment.update(exposed_ports: list.join(','))
+ end
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index fb4dbc1d..beefc5b1 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2021_05_19_134938) do
+ActiveRecord::Schema.define(version: 2021_06_02_071834) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
@@ -112,6 +112,7 @@ ActiveRecord::Schema.define(version: 2021_05_19_134938) do
t.integer "file_type_id"
t.integer "memory_limit"
t.boolean "network_enabled"
+ t.integer "cpu_limit"
end
create_table "exercise_collection_items", id: :serial, force: :cascade do |t|