Move "resolve" from Container to ContainerRequest.
Simplify state transition checking.
Improve formatting of error messages.
Rename workbench models to be singular.
Remove bogus factory files.
before_validation :fill_field_defaults, :if => :new_record?
before_validation :set_timestamps
validates :command, :container_image, :output_path, :cwd, :presence => true
+ validate :validate_state_change
validate :validate_change
after_save :request_finalize
after_save :process_tree_priority
(Cancelled = 'Cancelled')
]
- # Turn a container request into a container.
- def self.resolve req
- # In the future this will do things like resolve symbolic git and keep
- # references to content addresses.
- Container.create!({ :command => req.command,
- :container_image => req.container_image,
- :cwd => req.cwd,
- :environment => req.environment,
- :mounts => req.mounts,
- :output_path => req.output_path,
- :runtime_constraints => req.runtime_constraints })
+ State_transitions = {
+ nil => [Queued],
+ Queued => [Running, Cancelled],
+ Running => [Complete, Cancelled]
+ }
+
+ def state_transitions
+ State_transitions
end
def update_priority!
# its committed container requests and save the record.
max = 0
ContainerRequest.where(container_uuid: uuid).each do |cr|
- if cr.state == "Committed" and cr.priority > max
+ if cr.state == ContainerRequest::Committed and cr.priority > max
max = cr.priority
end
end
# permit priority change only.
permitted.push :priority
- if self.state_changed? and not self.state_was.nil?
- errors.add :state, "Can only go to from nil to Queued"
- end
-
when Running
if self.state_changed?
# At point of state change, can set state and started_at
- if self.state_was == Queued
- permitted.push :state, :started_at
- else
- errors.add :state, "Can only go from Queued to Running"
- end
-
+ permitted.push :state, :started_at
+ else
# While running, can update priority and progress.
permitted.push :priority, :progress
end
when Complete
if self.state_changed?
- if self.state_was == Running
- permitted.push :state, :finished_at, :output, :log
- else
- errors.add :state, "Cannot go from #{self.state_was} to #{self.state}"
- end
+ permitted.push :state, :finished_at, :output, :log
else
- errors.add :state, "Cannot update record in Complete state"
+ errors.add :state, "cannot update record"
end
when Cancelled
permitted.push :state, :finished_at, :output, :log
elsif self.state_was == Queued
permitted.push :state, :finished_at
- else
- errors.add :state, "Cannot go from #{self.state_was} to #{self.state}"
end
else
- errors.add :state, "Cannot update record in Cancelled state"
+ errors.add :state, "cannot update record"
end
else
- errors.add :state, "Invalid state #{self.state}"
+ errors.add :state, "invalid state"
end
check_update_whitelist permitted
end
def request_finalize
+ # This container is finished so finalize any associated container requests
+ # that are associated with this container.
if self.state_changed? and [Complete, Cancelled].include? self.state
act_as_system_user do
- ContainerRequest.where(container_uuid: uuid).each do |cr|
- cr.state = "Final"
- cr.save!
- end
+ # Note using update_all skips model validation and callbacks.
+ ContainerRequest.update_all({:state => ContainerRequest::Final}, ['container_uuid=?', uuid])
end
end
end
def process_tree_priority
- if self.priority_changed?
+ # This container is cancelled so cancel any container requests made by this
+ # container.
+ if self.priority_changed? and self.priority == 0
# This could propagate any parent priority to the children (not just
# priority 0)
- if self.priority == 0
- act_as_system_user do
- ContainerRequest.where(requesting_container_uuid: uuid).each do |cr|
- cr.priority = self.priority
- cr.save!
- end
+ act_as_system_user do
+ ContainerRequest.where(requesting_container_uuid: uuid).each do |cr|
+ cr.priority = self.priority
+ cr.save!
end
end
end
before_validation :fill_field_defaults, :if => :new_record?
before_validation :set_container
validates :command, :container_image, :output_path, :cwd, :presence => true
+ validate :validate_state_change
validate :validate_change
after_save :update_priority
(Final = 'Final'),
]
+ State_transitions = {
+ nil => [Uncommitted, Committed],
+ Uncommitted => [Committed],
+ Committed => [Final]
+ }
+
+ def state_transitions
+ State_transitions
+ end
+
def skip_uuid_read_permission_check
# XXX temporary until permissions are sorted out.
%w(modified_by_client_uuid container_uuid requesting_container_uuid)
self.priority ||= 1
end
+ # Turn a container request into a container.
+ def resolve
+ # In the future this will do things like resolve symbolic git and keep
+ # references to content addresses.
+ Container.create!({ :command => self.command,
+ :container_image => self.container_image,
+ :cwd => self.cwd,
+ :environment => self.environment,
+ :mounts => self.mounts,
+ :output_path => self.output_path,
+ :runtime_constraints => self.runtime_constraints })
+ end
+
def set_container
if self.container_uuid_changed?
if not current_user.andand.is_admin and not self.container_uuid.nil?
- errors.add :container_uuid, "Cannot only update container_uuid to nil."
+ errors.add :container_uuid, "can only be updated to nil."
end
else
if self.state_changed?
if self.state == Committed and (self.state_was == Uncommitted or self.state_was.nil?)
act_as_system_user do
- self.container_uuid = Container.resolve(self).andand.uuid
+ self.container_uuid = self.resolve.andand.uuid
end
end
end
when Committed
if container_uuid.nil?
- errors.add :container_uuid, "Has not been resolved to a container."
+ errors.add :container_uuid, "has not been resolved to a container."
end
# Can update priority, container count.
permitted.push :priority, :container_count_max, :container_uuid
if self.state_changed?
- if self.state_was == Uncommitted or self.state_was.nil?
- # Allow create-and-commit in a single operation.
- permitted.push :command, :container_image, :cwd, :description, :environment,
- :filters, :mounts, :name, :output_path, :properties,
- :requesting_container_uuid, :runtime_constraints,
- :state, :container_uuid
- else
- errors.add :state, "Can only go from Uncommitted to Committed"
- end
+ # Allow create-and-commit in a single operation.
+ permitted.push :command, :container_image, :cwd, :description, :environment,
+ :filters, :mounts, :name, :output_path, :properties,
+ :requesting_container_uuid, :runtime_constraints,
+ :state, :container_uuid
end
when Final
if self.state_changed?
- if self.state_was == Committed
permitted.push :state
- else
- errors.add :state, "Can only go from Committed to Final"
- end
else
- errors.add "Cannot update record in Final state"
+ errors.add :state, "does not allow updates"
end
else
- errors.add :state, "Invalid state #{self.state}"
+ errors.add :state, "invalid value"
end
check_update_whitelist permitted
t.string :log
t.text :environment
t.string :cwd
- t.string :command
+ t.text :command
t.string :output_path
- t.string :mounts
- t.string :runtime_constraints
+ t.text :mounts
+ t.text :runtime_constraints
t.string :output
t.string :container_image
t.float :progress
t.string :modified_by_user_uuid
t.string :name
t.text :description
- t.string :properties
+ t.text :properties
t.string :state
t.string :requesting_container_uuid
t.string :container_uuid
t.integer :container_count_max
- t.string :mounts
- t.string :runtime_constraints
+ t.text :mounts
+ t.text :runtime_constraints
t.string :container_image
- t.string :environment
+ t.text :environment
t.string :cwd
- t.string :command
+ t.text :command
t.string :output_path
t.integer :priority
t.datetime :expires_at
- t.string :filters
+ t.text :filters
t.timestamps
end
modified_by_user_uuid character varying(255),
name character varying(255),
description text,
- properties character varying(255),
+ properties text,
state character varying(255),
requesting_container_uuid character varying(255),
container_uuid character varying(255),
container_count_max integer,
- mounts character varying(255),
- runtime_constraints character varying(255),
+ mounts text,
+ runtime_constraints text,
container_image character varying(255),
- environment character varying(255),
+ environment text,
cwd character varying(255),
- command character varying(255),
+ command text,
output_path character varying(255),
priority integer,
expires_at timestamp without time zone,
- filters character varying(255),
+ filters text,
updated_at timestamp without time zone NOT NULL
);
log character varying(255),
environment text,
cwd character varying(255),
- command character varying(255),
+ command text,
output_path character varying(255),
- mounts character varying(255),
- runtime_constraints character varying(255),
+ mounts text,
+ runtime_constraints text,
output character varying(255),
container_image character varying(255),
progress double precision,
def check_update_whitelist permitted_fields
attribute_names.each do |field|
if not permitted_fields.include? field.to_sym and self.send((field.to_s + "_changed?").to_sym)
- errors.add field, "Illegal update of field #{field}"
+ errors.add field, "illegal update of field"
+ end
+ end
+ end
+
+ def validate_state_change
+ if self.state_changed?
+ unless state_transitions[self.state_was].andand.include? self.state
+ errors.add :state, "invalid state change from #{self.state_was} to #{self.state}"
+ return false
end
end
end
+++ /dev/null
-# Read about factories at https://github.com/thoughtbot/factory_girl
-
-FactoryGirl.define do
- factory :container_request do
- uuid "MyString"
- owner_uuid "MyString"
- created_at "2015-12-02 10:17:04"
- modified_at "2015-12-02 10:17:04"
- modified_by_client_uuid "MyString"
- modified_by_user_uuid "MyString"
- name "MyString"
- description "MyText"
- properties "MyString"
- state "MyString"
- requesting_container_uuid "MyString"
- container_uuid "MyString"
- container_count_max ""
- mounts "MyString"
- runtime_constraints "MyString"
- container_image "MyString"
- environment "MyString"
- cwd "MyString"
- command "MyString"
- output_path "MyString"
- priority ""
- expires_at "2015-12-02 10:17:04"
- filters "MyString"
- end
-end
+++ /dev/null
-# Read about factories at https://github.com/thoughtbot/factory_girl
-
-FactoryGirl.define do
- factory :container do
- uuid "MyString"
- owner_uuid "MyString"
- created_at "MyString"
- modified_at "MyString"
- modified_by_client_uuid "MyString"
- modified_by_user_uuid "MyString"
- state "MyString"
- started_at "2015-12-02 10:14:26"
- finished_at "2015-12-02 10:14:26"
- log "MyString"
- environment "MyText"
- cwd "MyString"
- command "MyString"
- output_path "MyString"
- mounts "MyString"
- runtime_constraints "MyString"
- output "MyString"
- container_image "MyString"
- progress 1.5
- priority ""
- end
-end
c.reload
t = Container.find_by_uuid c.container_uuid
- assert_equal t.command, ["echo", "foo"]
- assert_equal t.container_image, "img"
- assert_equal t.cwd, "/tmp"
- assert_equal t.environment, {}
- assert_equal t.mounts, {"BAR" => "FOO"}
- assert_equal t.output_path, "/tmpout"
- assert_equal t.runtime_constraints, {}
- assert_equal t.priority, 1
+ assert_equal ["echo", "foo"], t.command
+ assert_equal "img", t.container_image
+ assert_equal "/tmp", t.cwd
+ assert_equal({}, t.environment)
+ assert_equal({"BAR" => "FOO"}, t.mounts)
+ assert_equal "/tmpout", t.output_path
+ assert_equal({}, t.runtime_constraints)
+ assert_equal 1, t.priority
c.priority = 0
c.save!
c.reload
t.reload
- assert_equal c.priority, 0
- assert_equal t.priority, 0
+ assert_equal 0, c.priority
+ assert_equal 0, t.priority
end
c.save!
t = Container.find_by_uuid c.container_uuid
- assert_equal t.priority, 5
+ assert_equal 5, t.priority
c2 = ContainerRequest.new
c2.container_image = "img"
end
t.reload
- assert_equal t.priority, 10
+ assert_equal 10, t.priority
c2.reload
c2.priority = 0
c2.save!
t.reload
- assert_equal t.priority, 5
+ assert_equal 5, t.priority
c.reload
c.priority = 0
c.save!
t.reload
- assert_equal t.priority, 0
+ assert_equal 0, t.priority
end
c2.save!
t = Container.find_by_uuid c.container_uuid
- assert_equal t.priority, 5
+ assert_equal 5, t.priority
t2 = Container.find_by_uuid c2.container_uuid
- assert_equal t2.priority, 10
+ assert_equal 10, t2.priority
c.priority = 0
c.save!
t.reload
- assert_equal t.priority, 0
+ assert_equal 0, t.priority
t2.reload
- assert_equal t2.priority, 0
+ assert_equal 0, t2.priority
end
c2.save!
t = Container.find_by_uuid c.container_uuid
- assert_equal t.priority, 5
+ assert_equal 5, t.priority
t2 = Container.find_by_uuid c2.container_uuid
- assert_equal t2.priority, 10
+ assert_equal 10, t2.priority
c.priority = 0
c.save!
t.reload
- assert_equal t.priority, 0
+ assert_equal 0, t.priority
t2.reload
- assert_equal t2.priority, 10
+ assert_equal 10, t2.priority
end
test "Container container cancel" do
c.save!
c.reload
- assert_equal c.state, "Committed"
+ assert_equal "Committed", c.state
t = Container.find_by_uuid c.container_uuid
- assert_equal t.state, "Queued"
+ assert_equal "Queued", t.state
act_as_system_user do
t.state = "Cancelled"
end
c.reload
- assert_equal c.state, "Final"
+ assert_equal "Final", c.state
end
c.save!
c.reload
- assert_equal c.state, "Committed"
+ assert_equal "Committed", c.state
t = Container.find_by_uuid c.container_uuid
- assert_equal t.state, "Queued"
+ assert_equal "Queued", t.state
act_as_system_user do
t.state = "Running"
end
c.reload
- assert_equal c.state, "Committed"
+ assert_equal "Committed", c.state
act_as_system_user do
t.state = "Complete"
end
c.reload
- assert_equal c.state, "Final"
+ assert_equal "Final", c.state
end