When reusing a container, ensure log and output are readable.
include KindAndEtag
include CommonApiTemplate
include WhitelistUpdate
+ extend CurrentApiClient
serialize :environment, Hash
serialize :mounts, Hash
where('mounts = ?', self.deep_sort_hash(attrs[:mounts]).to_yaml).
where('runtime_constraints = ?', self.deep_sort_hash(attrs[:runtime_constraints]).to_yaml)
- # Check for Completed candidates that only had consistent outputs.
+ # Check for Completed candidates that had consistent outputs.
completed = candidates.where(state: Complete).where(exit_code: 0)
- if completed.select("output").group('output').limit(2).length == 1
- return completed.order('finished_at asc').limit(1).first
+ outputs = completed.select('output').group('output').limit(2)
+ if outputs.count.count != 1
+ Rails.logger.debug("Found #{outputs.count.length} different outputs")
+ elsif Collection.
+ readable_by(current_user).
+ where(portable_data_hash: outputs.first.output).
+ count < 1
+ Rails.logger.info("Found reusable container(s) " +
+ "but output #{outputs.first} is not readable " +
+ "by user #{current_user.uuid}")
+ else
+ # Return the oldest eligible container whose log is still
+ # present and readable by current_user.
+ readable_pdh = Collection.
+ readable_by(current_user).
+ select('portable_data_hash')
+ completed = completed.
+ where("log in (#{readable_pdh.to_sql})").
+ order('finished_at asc').
+ limit(1)
+ if completed.first
+ return completed.first
+ else
+ Rails.logger.info("Found reusable container(s) but none with a log " +
+ "readable by user #{current_user.uuid}")
+ end
end
# Check for Running candidates and return the most likely to finish sooner.
act_as_system_user do
# Notify container requests associated with this container
ContainerRequest.where(container_uuid: uuid,
- :state => ContainerRequest::Committed).each do |cr|
+ state: ContainerRequest::Committed).each do |cr|
cr.container_completed!
end
# Try to cancel any outstanding container requests made by this container.
ContainerRequest.where(requesting_container_uuid: uuid,
- :state => ContainerRequest::Committed).each do |cr|
+ state: ContainerRequest::Committed).each do |cr|
cr.priority = 0
cr.save
end
%w(modified_by_client_uuid container_uuid requesting_container_uuid)
end
+ # Finalize the container request after the container has
+ # finished/cancelled.
def container_completed!
- # may implement retry logic here in the future.
- self.state = ContainerRequest::Final
- self.save!
+ update_attributes!(state: ContainerRequest::Final)
+ c = Container.find_by_uuid(container_uuid)
+ ['output', 'log'].each do |out_type|
+ pdh = c.send(out_type)
+ next if pdh.nil?
+ manifest = Collection.where(portable_data_hash: pdh).first.manifest_text
+ Collection.create!(owner_uuid: owner_uuid,
+ manifest_text: manifest,
+ portable_data_hash: pdh,
+ name: "Container #{out_type} for request #{uuid}",
+ properties: {
+ 'type' => out_type,
+ 'container_request' => uuid,
+ })
+ end
end
protected
container_image: test
cwd: test
log: ea10d51bcf88862dbcc36eb292017dfd+45
- output: zzzzz-4zz18-znfnqtbbv4spc3w
+ output: 1f4b0bc7583c2a7f9102c395f4ffc5e3+45
output_path: test
command: ["echo", "hello"]
runtime_constraints:
finished_at: 2016-01-14 11:12:13.111111111 Z
container_image: test
cwd: test
- output: test
+ output: 1f4b0bc7583c2a7f9102c395f4ffc5e3+45
output_path: test
command: ["echo", "hello"]
runtime_constraints:
updated_at: 2016-01-11 11:11:11.111111111 Z
container_image: test
cwd: test
- output: test
+ output: 1f4b0bc7583c2a7f9102c395f4ffc5e3+45
output_path: test
command: ["echo", "hello"]
runtime_constraints:
updated_at: 2016-01-11 11:11:11.111111111 Z
container_image: test
cwd: test
- output: test
+ output: 1f4b0bc7583c2a7f9102c395f4ffc5e3+45
output_path: test
command: ["echo", "hello"]
runtime_constraints:
updated_at: 2016-01-11 11:11:11.111111111 Z
container_image: test
cwd: test
- output: test
+ output: 1f4b0bc7583c2a7f9102c395f4ffc5e3+45
output_path: test
command: ["echo", "hello"]
runtime_constraints:
test "Request is finalized when its container is completed" do
set_user_from_auth :active
- cr = create_minimal_req!(priority: 1, state: "Committed")
+ project = groups(:private)
+ cr = create_minimal_req!(owner_uuid: project.uuid,
+ priority: 1,
+ state: "Committed")
c = act_as_system_user do
c = Container.find_by_uuid(cr.container_uuid)
assert_equal "Committed", cr.state
act_as_system_user do
- c.update_attributes!(state: Container::Complete)
+ c.update_attributes!(state: Container::Complete,
+ output: '1f4b0bc7583c2a7f9102c395f4ffc5e3+45',
+ log: 'fa7aeb5140e2848d39b416daeef4ffc5+45')
end
cr.reload
assert_equal "Final", cr.state
+ ['output', 'log'].each do |out_type|
+ pdh = Container.find_by_uuid(cr.container_uuid).send(out_type)
+ assert_equal(1, Collection.where(portable_data_hash: pdh,
+ owner_uuid: project.uuid).count,
+ "Container #{out_type} should be copied to #{project.uuid}")
+ end
end
test "Container makes container request, then is cancelled" do
state: Container::Complete,
exit_code: 0,
log: 'ea10d51bcf88862dbcc36eb292017dfd+45',
- output: 'zzzzz-4zz18-znfnqtbbv4spc3w'
+ output: '1f4b0bc7583c2a7f9102c395f4ffc5e3+45'
}
c_older, _ = minimal_new(common_attrs)
completed_attrs = {
state: Container::Complete,
exit_code: 0,
- log: 'test',
+ log: 'ea10d51bcf88862dbcc36eb292017dfd+45',
}
c_output1, _ = minimal_new(common_attrs)
set_user_from_auth :dispatch1
c_output1.update_attributes!({state: Container::Locked})
c_output1.update_attributes!({state: Container::Running})
- c_output1.update_attributes!(completed_attrs.merge({output: 'output 1'}))
+ c_output1.update_attributes!(completed_attrs.merge({output: '1f4b0bc7583c2a7f9102c395f4ffc5e3+45'}))
c_output2.update_attributes!({state: Container::Locked})
c_output2.update_attributes!({state: Container::Running})
- c_output2.update_attributes!(completed_attrs.merge({output: 'output 2'}))
+ c_output2.update_attributes!(completed_attrs.merge({output: 'fa7aeb5140e2848d39b416daeef4ffc5+45'}))
reused = Container.find_reusable(common_attrs)
assert_nil reused
c_failed.update_attributes!({state: Container::Running})
c_failed.update_attributes!({state: Container::Complete,
exit_code: 42,
- log: "test",
- output: "test"})
+ log: 'ea10d51bcf88862dbcc36eb292017dfd+45',
+ output: 'ea10d51bcf88862dbcc36eb292017dfd+45'})
c_running.update_attributes!({state: Container::Locked})
c_running.update_attributes!({state: Container::Running,
progress: 0.15})
c_completed.update_attributes!({state: Container::Running})
c_completed.update_attributes!({state: Container::Complete,
exit_code: 0,
- log: "ea10d51bcf88862dbcc36eb292017dfd+45",
- output: "zzzzz-4zz18-znfnqtbbv4spc3w"})
+ log: 'ea10d51bcf88862dbcc36eb292017dfd+45',
+ output: '1f4b0bc7583c2a7f9102c395f4ffc5e3+45'})
c_running.update_attributes!({state: Container::Locked})
c_running.update_attributes!({state: Container::Running,
progress: 0.15})
reused = Container.find_reusable(common_attrs)
assert_not_nil reused
- assert_equal reused.uuid, c_completed.uuid
+ assert_equal c_completed.uuid, reused.uuid
end
test "find_reusable method should select running over locked container" do