require 'whitelist_update'
class ContainerRequest < ArvadosModel
+ include ArvadosModelUpdates
include HasUuid
include KindAndEtag
include CommonApiTemplate
include WhitelistUpdate
+ belongs_to :container, foreign_key: :container_uuid, primary_key: :uuid
+ belongs_to :requesting_container, {
+ class_name: 'Container',
+ foreign_key: :requesting_container_uuid,
+ primary_key: :uuid,
+ }
+
serialize :properties, Hash
serialize :environment, Hash
serialize :mounts, Hash
serialize :runtime_constraints, Hash
serialize :command, Array
serialize :scheduling_parameters, Hash
+ serialize :secret_mounts, Hash
before_validation :fill_field_defaults, :if => :new_record?
before_validation :validate_runtime_constraints
validates :priority, numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 1000 }
validate :validate_state_change
validate :check_update_whitelist
- after_save :update_priority
- after_save :finalize_if_needed
+ validate :secret_mounts_key_conflict
+ before_save :scrub_secret_mounts
before_create :set_requesting_container_uuid
before_destroy :set_priority_zero
+ after_save :update_priority
+ after_save :finalize_if_needed
api_accessible :user, extend: :common do |t|
t.add :command
AttrsPermittedAlways = [:owner_uuid, :state, :name, :description]
AttrsPermittedBeforeCommit = [:command, :container_count_max,
:container_image, :cwd, :environment, :filters, :mounts,
- :output_path, :priority, :properties, :requesting_container_uuid,
+ :output_path, :priority, :properties,
:runtime_constraints, :state, :container_uuid, :use_existing,
- :scheduling_parameters, :output_name, :output_ttl]
+ :scheduling_parameters, :secret_mounts, :output_name, :output_ttl]
def self.limit_index_columns_read
["mounts"]
end
+ def logged_attributes
+ super.except('secret_mounts')
+ end
+
def state_transitions
State_transitions
end
if state == Committed && Container.find_by_uuid(container_uuid).final?
reload
act_as_system_user do
- finalize!
+ leave_modified_by_user_alone do
+ finalize!
+ end
end
end
end
end
def self.full_text_searchable_columns
- super - ["mounts"]
+ super - ["mounts", "secret_mounts", "secret_mounts_md5"]
end
protected
if self.new_record? || self.state_was == Uncommitted
# Allow create-and-commit in a single operation.
- permitted.push *AttrsPermittedBeforeCommit
+ permitted.push(*AttrsPermittedBeforeCommit)
end
case self.state
end
when Final
- if self.state_changed? and not current_user.andand.is_admin
- self.errors.add :state, "of container request can only be set to Final by system."
- end
-
if self.state_was == Committed
- permitted.push :output_uuid, :log_uuid
+ # "Cancel" means setting priority=0, state=Committed
+ permitted.push :priority
+
+ if current_user.andand.is_admin
+ permitted.push :output_uuid, :log_uuid
+ end
end
end
super(permitted)
end
- def update_priority
- if self.state_changed? or
- self.priority_changed? or
- self.container_uuid_changed?
- act_as_system_user do
- Container.
- where('uuid in (?)',
- [self.container_uuid_was, self.container_uuid].compact).
- map(&:update_priority!)
+ def secret_mounts_key_conflict
+ secret_mounts.each do |k, v|
+ if mounts.has_key?(k)
+ errors.add(:secret_mounts, 'conflict with non-secret mounts')
+ return false
end
end
end
+ def scrub_secret_mounts
+ if self.state == Final
+ self.secret_mounts = {}
+ end
+ end
+
+ def update_priority
+ return unless state_changed? || priority_changed? || container_uuid_changed?
+ act_as_system_user do
+ ActiveRecord::Base.connection.execute('LOCK container_requests, containers IN EXCLUSIVE MODE')
+ Container.
+ where('uuid in (?)', [self.container_uuid_was, self.container_uuid].compact).
+ map(&:update_priority!)
+ end
+ end
+
def set_priority_zero
self.update_attributes!(priority: 0) if self.state != Final
end
def set_requesting_container_uuid
- return !new_record? if self.requesting_container_uuid # already set
-
- token_uuid = current_api_client_authorization.andand.uuid
- container = Container.where('auth_uuid=?', token_uuid).order('created_at desc').first
- if container
- self.requesting_container_uuid = container.uuid
- self.priority = container.priority
+ return if !current_api_client_authorization
+ ActiveRecord::Base.connection.execute('LOCK container_requests, containers IN EXCLUSIVE MODE')
+ if (c = Container.where('auth_uuid=?', current_api_client_authorization.uuid).select([:uuid, :priority]).first)
+ self.requesting_container_uuid = c.uuid
+ self.priority = c.priority>0 ? 1 : 0
end
- true
end
end