From: Peter Amstutz Date: Tue, 6 Sep 2016 20:56:53 +0000 (-0400) Subject: 9944: Add 'lockfile' to backports and update cwltool dependency X-Git-Tag: 1.1.0~750^2~3 X-Git-Url: https://git.arvados.org/arvados.git/commitdiff_plain/3db1a8bbb9f14799e0aabd12cff3c980c7da0167?hp=183d42044fbb540b7db6a093d25cca9808bfb369 9944: Add 'lockfile' to backports and update cwltool dependency --- diff --git a/apps/workbench/app/models/container_work_unit.rb b/apps/workbench/app/models/container_work_unit.rb index aac4ed8387..2580105f9f 100644 --- a/apps/workbench/app/models/container_work_unit.rb +++ b/apps/workbench/app/models/container_work_unit.rb @@ -46,13 +46,17 @@ class ContainerWorkUnit < ProxyWorkUnit end def can_cancel? - @proxied.is_a?(ContainerRequest) && state_label.in?(["Queued", "Locked", "Running"]) && priority > 0 + @proxied.is_a?(ContainerRequest) && @proxied.state == "Committed" && @proxied.priority > 0 && @proxied.editable? end def container_uuid get(:container_uuid) end + def priority + @proxied.priority + end + # For the following properties, use value from the @container if exists # This applies to a ContainerRequest with container_uuid @@ -92,10 +96,6 @@ class ContainerWorkUnit < ProxyWorkUnit get_combined(:runtime_constraints) end - def priority - get_combined(:priority) - end - def log_collection get_combined(:log) end @@ -134,13 +134,6 @@ class ContainerWorkUnit < ProxyWorkUnit [get_combined(:uuid), get(:uuid)].uniq end - def live_log_lines(limit=2000) - event_types = ["stdout", "stderr", "arv-mount", "crunch-run"] - log_lines = Log.where(event_type: event_types, object_uuid: log_object_uuids).order("id DESC").limit(limit) - log_lines.results.reverse. - flat_map { |log| log.properties[:text].split("\n") rescue [] } - end - def render_log collection = Collection.find(log_collection) rescue nil if collection @@ -155,7 +148,7 @@ class ContainerWorkUnit < ProxyWorkUnit end end - # End combined propeties + # End combined properties protected def get_combined key diff --git a/apps/workbench/app/models/job.rb b/apps/workbench/app/models/job.rb index 73f1f63be4..bf202c4eaa 100644 --- a/apps/workbench/app/models/job.rb +++ b/apps/workbench/app/models/job.rb @@ -43,8 +43,7 @@ class Job < ArvadosBase end def stderr_log_query(limit=nil) - query = Log.where(event_type: "stderr", object_uuid: self.uuid) - .order("id DESC") + query = Log.where(object_uuid: self.uuid).order("created_at DESC") query = query.limit(limit) if limit query end diff --git a/apps/workbench/app/models/proxy_work_unit.rb b/apps/workbench/app/models/proxy_work_unit.rb index feab5d8eb4..11ec0ee196 100644 --- a/apps/workbench/app/models/proxy_work_unit.rb +++ b/apps/workbench/app/models/proxy_work_unit.rb @@ -332,6 +332,15 @@ class ProxyWorkUnit < WorkUnit [uuid] end + def live_log_lines(limit) + Log.where(object_uuid: log_object_uuids). + order("created_at DESC"). + limit(limit). + select { |log| log.properties[:text].is_a? String }. + reverse. + flat_map { |log| log.properties[:text].split("\n") } + end + protected def get key, obj=@proxied diff --git a/apps/workbench/app/views/work_units/_show_child.html.erb b/apps/workbench/app/views/work_units/_show_child.html.erb index 1d5ae63785..acf19fd6b4 100644 --- a/apps/workbench/app/views/work_units/_show_child.html.erb +++ b/apps/workbench/app/views/work_units/_show_child.html.erb @@ -1,7 +1,6 @@
-
diff --git a/apps/workbench/app/views/work_units/_show_component.html.erb b/apps/workbench/app/views/work_units/_show_component.html.erb index 43f5692410..89233cfe00 100644 --- a/apps/workbench/app/views/work_units/_show_component.html.erb +++ b/apps/workbench/app/views/work_units/_show_component.html.erb @@ -1,46 +1,36 @@ <%# Work unit status %> -
- <%# Need additional handling for main object display %> - <% if @object.uuid == wu.uuid %> -
-
-
-
-
- <% if wu.is_running? and wu.child_summary_str %> - <%= wu.child_summary_str %> - <% end %> -
-
- <%= render partial: 'work_units/progress', locals: {wu: wu} %> -
-
- <% if wu.can_cancel? and @object.editable? %> - <%= form_tag "#{wu.uri}/cancel", remote: true, style: "display:inline; padding-left: 1em" do |f| %> - <%= hidden_field_tag :return_to, url_for(@object) %> - <%= button_tag "Cancel", {class: 'btn btn-xs btn-danger', id: "cancel-obj-button"} %> - <% end %> - <% end %> -
-
-
-
-
+
+
+ <% if wu.is_paused? %> +

+ This <%= wu.title %> is paused. Children that are already running + will continue to run, but no new processes will be submitted. +

<% end %> -
- <% if wu.is_paused? %> -

- This <%= wu.title %> is paused. Children that are already running - will continue to run, but no new processes will be submitted. -

+ <%= raw(wu.show_runtime) %> +
+ <%# Need additional handling for main object display %> + <% if @object.uuid == wu.uuid %> +
+ <% if wu.is_running? and wu.child_summary_str %> + <%= wu.child_summary_str %> <% end %> - - <%= raw(wu.show_runtime) %>
-
+
+ <%= render partial: 'work_units/progress', locals: {wu: wu} %> +
+
+ <% if wu.can_cancel? and @object.editable? %> + <%= form_tag "#{wu.uri}/cancel", remote: true, style: "display:inline; padding-left: 1em" do |f| %> + <%= hidden_field_tag :return_to, url_for(@object) %> + <%= button_tag "Cancel", {class: 'btn btn-xs btn-danger', id: "cancel-obj-button"} %> + <% end %> + <% end %> +
+ <% end %> +

<%= render(partial: 'work_units/component_detail', locals: {current_obj: wu}) %> diff --git a/apps/workbench/test/integration/application_layout_test.rb b/apps/workbench/test/integration/application_layout_test.rb index f4221a91d8..93827a9c74 100644 --- a/apps/workbench/test/integration/application_layout_test.rb +++ b/apps/workbench/test/integration/application_layout_test.rb @@ -286,79 +286,4 @@ class ApplicationLayoutTest < ActionDispatch::IntegrationTest end end end - - [ - ['jobs', 'running_job_with_components', true], - ['pipeline_instances', 'components_is_jobspec', false], - ['containers', 'running', false], - ['container_requests', 'running', true], - ].each do |type, fixture, cancelable| - test "cancel button for #{type}/#{fixture}" do - if cancelable - need_selenium 'to cancel' - end - - obj = api_fixture(type)[fixture] - visit page_with_token "active", "/#{type}/#{obj['uuid']}" - - assert_text 'created_at' - if cancelable - assert page.has_button?('Cancel'), 'No Cancel button' - click_button 'Cancel' - wait_for_ajax - assert page.has_no_button?('Cancel'), 'Cancel button not expected after clicking' - else - assert page.has_no_button?('Cancel'), 'Cancel button not expected' - end - end - end - - [ - ['jobs', 'running_job_with_components'], - ['pipeline_instances', 'has_component_with_completed_jobs'], - ['container_requests', 'running'], - ['container_requests', 'completed'], - ].each do |type, fixture| - test "edit description for #{type}/#{fixture}" do - obj = api_fixture(type)[fixture] - visit page_with_token "active", "/#{type}/#{obj['uuid']}" - - within('.arv-description-as-subtitle') do - find('.fa-pencil').click - find('.editable-input textarea').set('*Textile description for object*') - find('.editable-submit').click - end - wait_for_ajax - - # verify description - assert page.has_no_text? '*Textile description for object*' - assert page.has_text? 'Textile description for object' - end - end - - [ - ['Two Part Pipeline Template', 'part-one', 'Provide a value for the following'], - ['Workflow with input specifications', 'this workflow has inputs specified', 'Provide a value for the following'], - ].each do |template_name, preview_txt, process_txt| - test "run a process using template #{template_name} from dashboard" do - visit page_with_token('admin') - assert_text 'Recent pipelines and processes' # seeing dashboard now - - within('.recent-processes-actions') do - assert page.has_link?('All processes') - find('a', text: 'Run a pipeline').click - end - - # in the chooser, verify preview and click Next button - within('.modal-dialog') do - find('.selectable', text: template_name).click - assert_text preview_txt - find('.btn', text: 'Next: choose inputs').click - end - - # in the process page now - assert_text process_txt - assert_selector 'a', text: template_name - end - end end diff --git a/apps/workbench/test/integration/container_requests_test.rb b/apps/workbench/test/integration/container_requests_test.rb index cdb5c9180b..df6584ebb6 100644 --- a/apps/workbench/test/integration/container_requests_test.rb +++ b/apps/workbench/test/integration/container_requests_test.rb @@ -75,7 +75,7 @@ class ContainerRequestsTest < ActionDispatch::IntegrationTest assert_text 'This workflow does not need any further inputs' click_link "Run" wait_for_ajax - assert_text 'This container is committed' + assert_text 'This container is queued' end end @@ -94,6 +94,6 @@ class ContainerRequestsTest < ActionDispatch::IntegrationTest page.assert_no_selector 'a.disabled,button.disabled', text: 'Run' click_link "Run" wait_for_ajax - assert_text 'This container is committed' + assert_text 'This container is queued' end end diff --git a/apps/workbench/test/integration/websockets_test.rb b/apps/workbench/test/integration/websockets_test.rb index e9f5a799c0..38917f05ae 100644 --- a/apps/workbench/test/integration/websockets_test.rb +++ b/apps/workbench/test/integration/websockets_test.rb @@ -3,68 +3,55 @@ require 'integration_helper' class WebsocketTest < ActionDispatch::IntegrationTest setup do need_selenium "to make websockets work" + @dispatch_client = ArvadosApiClient.new + end + + def dispatch_log(body) + use_token :dispatch1 do + @dispatch_client.api('logs', '', log: body) + end end test "test page" do - visit(page_with_token("admin", "/websockets")) + visit(page_with_token("active", "/websockets")) fill_in("websocket-message-content", :with => "Stuff") click_button("Send") assert_text '"status":400' end - test "test live logging" do - visit(page_with_token("admin", "/pipeline_instances/zzzzz-d1hrv-9fm8l10i9z2kqc6")) - click_link("Log") - assert_no_text '123 hello' - - api = ArvadosApiClient.new + [ + ['pipeline_instances', 'pipeline_in_running_state', api_fixture('jobs')['running']], + ['jobs', 'running'], + ['containers', 'running'], + ['container_requests', 'running', api_fixture('containers')['running']], + ].each do |controller, view_fixture_name, log_target_fixture| + view_fixture = api_fixture(controller)[view_fixture_name] + log_target_fixture ||= view_fixture - Thread.current[:arvados_api_token] = @@API_AUTHS["admin"]['api_token'] - api.api("logs", "", {log: { - object_uuid: "zzzzz-d1hrv-9fm8l10i9z2kqc6", - event_type: "stderr", - properties: {"text" => "123 hello"}}}) - assert_text '123 hello' - end + test "test live logging and scrolling for #{controller}" do - [ - ["pipeline_instances", api_fixture("pipeline_instances")['pipeline_with_newer_template']['uuid']], - ["jobs", api_fixture("jobs")['running']['uuid']], - ["containers", api_fixture("containers")['running']['uuid']], - ["container_requests", api_fixture("container_requests")['running']['uuid'], api_fixture("containers")['running']['uuid']], - ].each do |c| - test "test live logging scrolling #{c[0]}" do - - controller = c[0] - uuid = c[1] - log_uuid = c[2] || c[1] - - visit(page_with_token("admin", "/#{controller}/#{uuid}")) - click_link("Log") + visit(page_with_token("active", "/#{controller}/#{view_fixture['uuid']}\#Log")) assert_no_text '123 hello' - api = ArvadosApiClient.new - text = "" (1..1000).each do |i| text << "#{i} hello\n" end - Thread.current[:arvados_api_token] = @@API_AUTHS["admin"]['api_token'] - api.api("logs", "", {log: { - object_uuid: log_uuid, - event_type: "stderr", - properties: {"text" => text}}}) + dispatch_log(owner_uuid: log_target_fixture['owner_uuid'], + object_uuid: log_target_fixture['uuid'], + event_type: "stderr", + properties: {"text" => text}) assert_text '1000 hello' # First test that when we're already at the bottom of the page, it scrolls down # when a new line is added. old_top = page.evaluate_script("$('#event_log_div').scrollTop()") - api.api("logs", "", {log: { - object_uuid: log_uuid, - event_type: "stderr", - properties: {"text" => "1001 hello\n"}}}) + dispatch_log(owner_uuid: log_target_fixture['owner_uuid'], + object_uuid: log_target_fixture['uuid'], + event_type: "dispatch", + properties: {"text" => "1001 hello\n"}) assert_text '1001 hello' # Check that new value of scrollTop is greater than the old one @@ -75,10 +62,10 @@ class WebsocketTest < ActionDispatch::IntegrationTest page.execute_script "$('#event_log_div').scrollTop(30)" assert_equal 30, page.evaluate_script("$('#event_log_div').scrollTop()") - api.api("logs", "", {log: { - object_uuid: log_uuid, - event_type: "stderr", - properties: {"text" => "1002 hello\n"}}}) + dispatch_log(owner_uuid: log_target_fixture['owner_uuid'], + object_uuid: log_target_fixture['uuid'], + event_type: "stdout", + properties: {"text" => "1002 hello\n"}) assert_text '1002 hello' # Check that we haven't changed scroll position @@ -87,26 +74,26 @@ class WebsocketTest < ActionDispatch::IntegrationTest end test "pipeline instance arv-refresh-on-log-event" do - Thread.current[:arvados_api_token] = @@API_AUTHS["admin"]['api_token'] # Do something and check that the pane reloads. - p = PipelineInstance.create({state: "RunningOnServer", - components: { - c1: { - script: "test_hash.py", - script_version: "1de84a854e2b440dc53bf42f8548afa4c17da332" - } - } - }) - - visit(page_with_token("admin", "/pipeline_instances/#{p.uuid}")) + p = use_token :active do + PipelineInstance.create(state: "RunningOnServer", + components: { + c1: { + script: "test_hash.py", + script_version: "1de84a854e2b440dc53bf42f8548afa4c17da332" + } + }) + end + visit(page_with_token("active", "/pipeline_instances/#{p.uuid}")) assert_text 'Active' assert page.has_link? 'Pause' assert_no_text 'Complete' assert page.has_no_link? 'Re-run with latest' - p.state = "Complete" - p.save! + use_token :dispatch1 do + p.update_attributes!(state: 'Complete') + end assert_no_text 'Active' assert page.has_no_link? 'Pause' @@ -115,35 +102,34 @@ class WebsocketTest < ActionDispatch::IntegrationTest end test "job arv-refresh-on-log-event" do - Thread.current[:arvados_api_token] = @@API_AUTHS["admin"]['api_token'] # Do something and check that the pane reloads. - p = Job.where(uuid: api_fixture('jobs')['running_will_be_completed']['uuid']).results.first - - visit(page_with_token("admin", "/jobs/#{p.uuid}")) + uuid = api_fixture('jobs')['running_will_be_completed']['uuid'] + visit(page_with_token("active", "/jobs/#{uuid}")) assert_no_text 'complete' assert_no_text 'Re-run job' - p.state = "Complete" - p.save! + use_token :dispatch1 do + Job.find(uuid).update_attributes!(state: 'Complete') + end assert_text 'complete' assert_text 'Re-run job' end test "dashboard arv-refresh-on-log-event" do - Thread.current[:arvados_api_token] = @@API_AUTHS["admin"]['api_token'] - - visit(page_with_token("admin", "/")) + visit(page_with_token("active", "/")) assert_no_text 'test dashboard arv-refresh-on-log-event' # Do something and check that the pane reloads. - p = PipelineInstance.create({state: "RunningOnServer", - name: "test dashboard arv-refresh-on-log-event", - components: { - } - }) + use_token :active do + p = PipelineInstance.create({state: "RunningOnServer", + name: "test dashboard arv-refresh-on-log-event", + components: { + } + }) + end assert_text 'test dashboard arv-refresh-on-log-event' end @@ -179,13 +165,10 @@ class WebsocketTest < ActionDispatch::IntegrationTest text = "2014-11-07_23:33:51 #{uuid} 31708 1 stderr crunchstat: cpu 1970.8200 user 60.2700 sys 8 cpus -- interval 10.0002 seconds 35.3900 user 0.8600 sys" assert_triggers_dom_event 'arv-log-event' do - use_token :active do - api = ArvadosApiClient.new - api.api("logs", "", {log: { - object_uuid: uuid, - event_type: "stderr", - properties: {"text" => text}}}) - end + dispatch_log(owner_uuid: api_fixture('jobs')['running']['owner_uuid'], + object_uuid: uuid, + event_type: "stderr", + properties: {"text" => text}) end # Graph should have appeared (even if it hadn't above). It's @@ -217,65 +200,56 @@ class WebsocketTest < ActionDispatch::IntegrationTest end test "test running job with just a few previous log records" do - Thread.current[:arvados_api_token] = @@API_AUTHS["admin"]['api_token'] - job = Job.where(uuid: api_fixture("jobs")['running']['uuid']).results.first - visit page_with_token("admin", "/jobs/#{job.uuid}") - - api = ArvadosApiClient.new + job = api_fixture("jobs")['running'] # Create just one old log record - api.api("logs", "", {log: { - object_uuid: job.uuid, - event_type: "stderr", - properties: {"text" => "Historic log message"}}}) + dispatch_log(owner_uuid: job['owner_uuid'], + object_uuid: job['uuid'], + event_type: "stderr", + properties: {"text" => "Historic log message"}) - click_link("Log") + visit page_with_token("active", "/jobs/#{job['uuid']}\#Log") # Expect "all" historic log records because we have less than # default Rails.configuration.running_job_log_records_to_fetch count assert_text 'Historic log message' # Create new log record and expect it to show up in log tab - api.api("logs", "", {log: { - object_uuid: job.uuid, - event_type: "stderr", - properties: {"text" => "Log message after subscription"}}}) + dispatch_log(owner_uuid: job['owner_uuid'], + object_uuid: job['uuid'], + event_type: "stderr", + properties: {"text" => "Log message after subscription"}) assert_text 'Log message after subscription' end test "test running job with too many previous log records" do - Rails.configuration.running_job_log_records_to_fetch = 5 - - Thread.current[:arvados_api_token] = @@API_AUTHS["admin"]['api_token'] - job = Job.where(uuid: api_fixture("jobs")['running']['uuid']).results.first - - visit page_with_token("admin", "/jobs/#{job.uuid}") - - api = ArvadosApiClient.new - - # Create Rails.configuration.running_job_log_records_to_fetch + 1 log records - (0..Rails.configuration.running_job_log_records_to_fetch).each do |count| - api.api("logs", "", {log: { - object_uuid: job.uuid, - event_type: "stderr", - properties: {"text" => "Old log message #{count}"}}}) + max = 5 + Rails.configuration.running_job_log_records_to_fetch = max + job = api_fixture("jobs")['running'] + + # Create max+1 log records + (0..max).each do |count| + dispatch_log(owner_uuid: job['owner_uuid'], + object_uuid: job['uuid'], + event_type: "stderr", + properties: {"text" => "Old log message #{count}"}) end - # Go to log tab, which results in subscribing to websockets - click_link("Log") + visit page_with_token("active", "/jobs/#{job['uuid']}\#Log") # Expect all but the first historic log records, # because that was one too many than fetch count. - (1..Rails.configuration.running_job_log_records_to_fetch).each do |count| + (1..max).each do |count| assert_text "Old log message #{count}" end assert_no_text 'Old log message 0' # Create one more log record after subscription - api.api("logs", "", {log: { - object_uuid: job.uuid, - event_type: "stderr", - properties: {"text" => "Life goes on!"}}}) + dispatch_log(owner_uuid: job['owner_uuid'], + object_uuid: job['uuid'], + event_type: "stderr", + properties: {"text" => "Life goes on!"}) + # Expect it to show up in log tab assert_text 'Life goes on!' end diff --git a/apps/workbench/test/integration/work_units_test.rb b/apps/workbench/test/integration/work_units_test.rb index 63ba275e6b..7d19fcc9d7 100644 --- a/apps/workbench/test/integration/work_units_test.rb +++ b/apps/workbench/test/integration/work_units_test.rb @@ -55,4 +55,77 @@ class WorkUnitsTest < ActionDispatch::IntegrationTest assert_no_selector "a[href=\"#{link}\"]" end end + + [ + ['jobs', 'running_job_with_components', true], + ['pipeline_instances', 'components_is_jobspec', false], + ['containers', 'running', false], + ['container_requests', 'running', true], + ].each do |type, fixture, cancelable| + test "cancel button for #{type}/#{fixture}" do + if cancelable + need_selenium 'to cancel' + end + + obj = api_fixture(type)[fixture] + visit page_with_token "active", "/#{type}/#{obj['uuid']}" + + assert_text 'created_at' + if cancelable + assert_selector 'button', text: 'Cancel' + click_button 'Cancel' + wait_for_ajax + end + assert_no_selector 'button', text: 'Cancel' + end + end + + [ + ['jobs', 'running_job_with_components'], + ['pipeline_instances', 'has_component_with_completed_jobs'], + ['container_requests', 'running'], + ['container_requests', 'completed'], + ].each do |type, fixture| + test "edit description for #{type}/#{fixture}" do + obj = api_fixture(type)[fixture] + visit page_with_token "active", "/#{type}/#{obj['uuid']}" + + within('.arv-description-as-subtitle') do + find('.fa-pencil').click + find('.editable-input textarea').set('*Textile description for object*') + find('.editable-submit').click + end + wait_for_ajax + + # verify description + assert page.has_no_text? '*Textile description for object*' + assert page.has_text? 'Textile description for object' + end + end + + [ + ['Two Part Pipeline Template', 'part-one', 'Provide a value for the following'], + ['Workflow with input specifications', 'this workflow has inputs specified', 'Provide a value for the following'], + ].each do |template_name, preview_txt, process_txt| + test "run a process using template #{template_name} from dashboard" do + visit page_with_token('admin') + assert_text 'Recent pipelines and processes' # seeing dashboard now + + within('.recent-processes-actions') do + assert page.has_link?('All processes') + find('a', text: 'Run a pipeline').click + end + + # in the chooser, verify preview and click Next button + within('.modal-dialog') do + find('.selectable', text: template_name).click + assert_text preview_txt + find('.btn', text: 'Next: choose inputs').click + end + + # in the process page now + assert_text process_txt + assert_selector 'a', text: template_name + end + end end diff --git a/apps/workbench/test/integration_helper.rb b/apps/workbench/test/integration_helper.rb index 785912d324..c94fc619f6 100644 --- a/apps/workbench/test/integration_helper.rb +++ b/apps/workbench/test/integration_helper.rb @@ -5,10 +5,20 @@ require 'uri' require 'yaml' def available_port for_what - Addrinfo.tcp("0.0.0.0", 0).listen do |srv| - port = srv.connect_address.ip_port - STDERR.puts "Using port #{port} for #{for_what}" - return port + begin + Addrinfo.tcp("0.0.0.0", 0).listen do |srv| + port = srv.connect_address.ip_port + # Selenium needs an additional locking port, check if it's available + # and retry if necessary. + if for_what == 'selenium' + locking_port = port - 1 + Addrinfo.tcp("0.0.0.0", locking_port).listen.close + end + STDERR.puts "Using port #{port} for #{for_what}" + return port + end + rescue Errno::EADDRINUSE, Errno::EACCES + retry end end diff --git a/apps/workbench/test/test_helper.rb b/apps/workbench/test/test_helper.rb index 78ef2d21f1..b8aa82e1af 100644 --- a/apps/workbench/test/test_helper.rb +++ b/apps/workbench/test/test_helper.rb @@ -31,15 +31,17 @@ class ActiveSupport::TestCase # Note: You'll currently still have to declare fixtures explicitly # in integration tests -- they do not yet inherit this setting fixtures :all - def use_token token_name - was = Thread.current[:arvados_api_token] + def use_token(token_name) + user_was = Thread.current[:user] + token_was = Thread.current[:arvados_api_token] auth = api_fixture('api_client_authorizations')[token_name.to_s] Thread.current[:arvados_api_token] = auth['api_token'] if block_given? begin yield ensure - Thread.current[:arvados_api_token] = was + Thread.current[:user] = user_was + Thread.current[:arvados_api_token] = token_was end end end diff --git a/apps/workbench/test/unit/work_unit_test.rb b/apps/workbench/test/unit/work_unit_test.rb index 304dc8ba32..68bc2fdadd 100644 --- a/apps/workbench/test/unit/work_unit_test.rb +++ b/apps/workbench/test/unit/work_unit_test.rb @@ -104,4 +104,18 @@ class WorkUnitTest < ActiveSupport::TestCase end end end + + test 'can_cancel?' do + use_token 'active' do + assert find_fixture(Job, 'running').work_unit.can_cancel? + refute find_fixture(Container, 'running').work_unit.can_cancel? + assert find_fixture(ContainerRequest, 'running').work_unit.can_cancel? + end + use_token 'spectator' do + refute find_fixture(ContainerRequest, 'running_anonymous_accessible').work_unit.can_cancel? + end + use_token 'admin' do + assert find_fixture(ContainerRequest, 'running_anonymous_accessible').work_unit.can_cancel? + end + end end diff --git a/build/run-build-packages.sh b/build/run-build-packages.sh index 7e4a3d4c11..5c78de7d8d 100755 --- a/build/run-build-packages.sh +++ b/build/run-build-packages.sh @@ -100,33 +100,33 @@ case "$TARGET" in FORMAT=deb PYTHON_BACKPORTS=(python-gflags==2.0 google-api-python-client==1.4.2 \ oauth2client==1.5.2 pyasn1==0.1.7 pyasn1-modules==0.0.5 \ - rsa uritemplate httplib2 ws4py pykka six pyexecjs jsonschema \ + rsa uritemplate httplib2 ws4py pykka six \ ciso8601 pycrypto backports.ssl_match_hostname llfuse==0.41.1 \ 'pycurl<7.21.5' contextlib2 pyyaml 'rdflib>=4.2.0' \ shellescape mistune typing avro ruamel.ordereddict - cachecontrol cwltest) + cachecontrol) PYTHON3_BACKPORTS=(docker-py==1.7.2 six requests websocket-client) ;; debian8) FORMAT=deb PYTHON_BACKPORTS=(python-gflags==2.0 google-api-python-client==1.4.2 \ oauth2client==1.5.2 pyasn1==0.1.7 pyasn1-modules==0.0.5 \ - rsa uritemplate httplib2 ws4py pykka six pyexecjs jsonschema \ + rsa uritemplate httplib2 ws4py pykka six \ ciso8601 pycrypto backports.ssl_match_hostname llfuse==0.41.1 \ 'pycurl<7.21.5' pyyaml 'rdflib>=4.2.0' \ shellescape mistune typing avro ruamel.ordereddict - cachecontrol cwltest) + cachecontrol) PYTHON3_BACKPORTS=(docker-py==1.7.2 six requests websocket-client) ;; ubuntu1204) FORMAT=deb PYTHON_BACKPORTS=(python-gflags==2.0 google-api-python-client==1.4.2 \ oauth2client==1.5.2 pyasn1==0.1.7 pyasn1-modules==0.0.5 \ - rsa uritemplate httplib2 ws4py pykka six pyexecjs jsonschema \ + rsa uritemplate httplib2 ws4py pykka six \ ciso8601 pycrypto backports.ssl_match_hostname llfuse==0.41.1 \ contextlib2 'pycurl<7.21.5' pyyaml 'rdflib>=4.2.0' \ shellescape mistune typing avro isodate ruamel.ordereddict - cachecontrol cwltest) + cachecontrol) PYTHON3_BACKPORTS=(docker-py==1.7.2 six requests websocket-client) ;; ubuntu1404) @@ -135,7 +135,7 @@ case "$TARGET" in google-api-python-client==1.4.2 six uritemplate oauth2client==1.5.2 httplib2 \ rsa 'pycurl<7.21.5' backports.ssl_match_hostname pyyaml 'rdflib>=4.2.0' \ shellescape mistune typing avro ruamel.ordereddict - cachecontrol cwltest) + cachecontrol) PYTHON3_BACKPORTS=(docker-py==1.7.2 requests websocket-client) ;; centos6) @@ -150,12 +150,12 @@ case "$TARGET" in PYTHON3_INSTALL_LIB=lib/python$PYTHON3_VERSION/site-packages PYTHON_BACKPORTS=(python-gflags==2.0 google-api-python-client==1.4.2 \ oauth2client==1.5.2 pyasn1==0.1.7 pyasn1-modules==0.0.5 \ - rsa uritemplate httplib2 ws4py pykka six pyexecjs jsonschema \ + rsa uritemplate httplib2 ws4py pykka six \ ciso8601 pycrypto backports.ssl_match_hostname 'pycurl<7.21.5' \ - python-daemon lockfile llfuse==0.41.1 'pbr<1.0' pyyaml \ + python-daemon llfuse==0.41.1 'pbr<1.0' pyyaml \ 'rdflib>=4.2.0' shellescape mistune typing avro requests \ isodate pyparsing sparqlwrapper html5lib==0.9999999 keepalive \ - ruamel.ordereddict cachecontrol cwltest) + ruamel.ordereddict cachecontrol) PYTHON3_BACKPORTS=(docker-py==1.7.2 six requests websocket-client) export PYCURL_SSL_LIBRARY=nss ;; @@ -170,12 +170,12 @@ case "$TARGET" in PYTHON3_INSTALL_LIB=lib/python$PYTHON3_VERSION/site-packages PYTHON_BACKPORTS=(python-gflags==2.0 google-api-python-client==1.4.2 \ oauth2client==1.5.2 pyasn1==0.1.7 pyasn1-modules==0.0.5 \ - rsa uritemplate httplib2 ws4py pykka pyexecjs jsonschema \ + rsa uritemplate httplib2 ws4py pykka \ ciso8601 pycrypto 'pycurl<7.21.5' \ python-daemon llfuse==0.41.1 'pbr<1.0' pyyaml \ 'rdflib>=4.2.0' shellescape mistune typing avro \ isodate pyparsing sparqlwrapper html5lib==0.9999999 keepalive \ - ruamel.ordereddict cachecontrol cwltest) + ruamel.ordereddict cachecontrol) PYTHON3_BACKPORTS=(docker-py==1.7.2 six requests websocket-client) export PYCURL_SSL_LIBRARY=nss ;; @@ -448,6 +448,8 @@ cd $WORKSPACE/packages/$TARGET rm -rf "$WORKSPACE/sdk/cwl/build" fpm_build $WORKSPACE/sdk/cwl "${PYTHON2_PKG_PREFIX}-arvados-cwl-runner" 'Curoverse, Inc.' 'python' "$(awk '($1 == "Version:"){print $2}' $WORKSPACE/sdk/cwl/arvados_cwl_runner.egg-info/PKG-INFO)" "--url=https://arvados.org" "--description=The Arvados CWL runner" --iteration 3 +fpm_build lockfile "" "" python 0.12.2 --epoch 1 + # schema_salad. This is a python dependency of arvados-cwl-runner, # but we can't use the usual PYTHONPACKAGES way to build this package due to the # intricacies of how version numbers get generated in setup.py: we need version @@ -463,14 +465,18 @@ fpm_build $WORKSPACE/sdk/cwl "${PYTHON2_PKG_PREFIX}-arvados-cwl-runner" 'Curover # So we build this thing separately. # # Ward, 2016-03-17 -fpm_build schema_salad "" "" python 1.17.20160820171034 +fpm_build schema_salad "" "" python 1.18.20160907135919 --depends "python-lockfile >= 1:0.12.2-2" # And schema_salad now depends on ruamel-yaml, which apparently has a braindead setup.py that requires special arguments to build (otherwise, it aborts with 'error: you have to install with "pip install ."'). Sigh. # Ward, 2016-05-26 fpm_build ruamel.yaml "" "" python 0.12.4 --python-setup-py-arguments "--single-version-externally-managed" +# Dependency of cwltool. Fpm doesn't produce a package with the correct version +# number unless we build it explicitly +fpm_build cwltest "" "" python 1.0.20160907111242 + # And for cwltool we have the same problem as for schema_salad. Ward, 2016-03-17 -fpm_build cwltool "" "" python 1.0.20160901133827 +fpm_build cwltool "" "" python 1.0.20160907141844 # FPM eats the trailing .0 in the python-rdflib-jsonld package when built with 'rdflib-jsonld>=0.3.0'. Force the version. Ward, 2016-03-25 fpm_build rdflib-jsonld "" "" python 0.3.0 diff --git a/sdk/cwl/setup.py b/sdk/cwl/setup.py index 86b24a394e..bd9ea6e98c 100644 --- a/sdk/cwl/setup.py +++ b/sdk/cwl/setup.py @@ -32,8 +32,8 @@ setup(name='arvados-cwl-runner', # Make sure to update arvados/build/run-build-packages.sh as well # when updating the cwltool version pin. install_requires=[ - 'cwltool==1.0.20160901133827', - 'arvados-python-client>=0.1.20160714204738', + 'cwltool==1.0.20160907141844', + 'arvados-python-client>=0.1.20160714204738' ], data_files=[ ('share/doc/arvados-cwl-runner', ['LICENSE-2.0.txt', 'README.rst']), diff --git a/services/api/app/controllers/arvados/v1/container_requests_controller.rb b/services/api/app/controllers/arvados/v1/container_requests_controller.rb index fe4696e300..6e2848ceb5 100644 --- a/services/api/app/controllers/arvados/v1/container_requests_controller.rb +++ b/services/api/app/controllers/arvados/v1/container_requests_controller.rb @@ -3,4 +3,5 @@ class Arvados::V1::ContainerRequestsController < ApplicationController accept_attribute_as_json :mounts, Hash accept_attribute_as_json :runtime_constraints, Hash accept_attribute_as_json :command, Array + accept_attribute_as_json :filters, Array end diff --git a/services/api/app/models/arvados_model.rb b/services/api/app/models/arvados_model.rb index 5a6ce0af81..38bd5cfb34 100644 --- a/services/api/app/models/arvados_model.rb +++ b/services/api/app/models/arvados_model.rb @@ -193,61 +193,43 @@ class ArvadosModel < ActiveRecord::Base return self end - # Collect the uuids for each user and any groups readable by each user. + # Collect the UUIDs of the authorized users. user_uuids = users_list.map { |u| u.uuid } - uuid_list = user_uuids + users_list.flat_map { |u| u.groups_i_can(:read) } + + # Collect the UUIDs of all groups readable by any of the + # authorized users. If one of these (or the UUID of one of the + # authorized users themselves) is an object's owner_uuid, that + # object is readable. + owner_uuids = user_uuids + users_list.flat_map { |u| u.groups_i_can(:read) } + owner_uuids.uniq! + sql_conds = [] - sql_params = [] sql_table = kwargs.fetch(:table_name, table_name) - or_object_uuid = '' - - # This row is owned by a member of users_list, or owned by a group - # readable by a member of users_list - # or - # This row uuid is the uuid of a member of users_list - # or - # A permission link exists ('write' and 'manage' implicitly include - # 'read') from a member of users_list, or a group readable by users_list, - # to this row, or to the owner of this row (see join() below). - sql_conds += ["#{sql_table}.uuid in (?)"] - sql_params += [user_uuids] - - if uuid_list.any? - sql_conds += ["#{sql_table}.owner_uuid in (?)"] - sql_params += [uuid_list] - - sanitized_uuid_list = uuid_list. - collect { |uuid| sanitize(uuid) }.join(', ') - permitted_uuids = "(SELECT head_uuid FROM links WHERE link_class='permission' AND tail_uuid IN (#{sanitized_uuid_list}))" - sql_conds += ["#{sql_table}.uuid IN #{permitted_uuids}"] - end - - if sql_table == "links" and users_list.any? - # This row is a 'permission' or 'resources' link class - # The uuid for a member of users_list is referenced in either the head - # or tail of the link - sql_conds += ["(#{sql_table}.link_class in (#{sanitize 'permission'}, #{sanitize 'resources'}) AND (#{sql_table}.head_uuid IN (?) OR #{sql_table}.tail_uuid IN (?)))"] - sql_params += [user_uuids, user_uuids] - end - - if sql_table == "logs" and users_list.any? - # Link head points to the object described by this row - sql_conds += ["#{sql_table}.object_uuid IN #{permitted_uuids}"] - - # This object described by this row is owned by this user, or owned by a group readable by this user - sql_conds += ["#{sql_table}.object_owner_uuid in (?)"] - sql_params += [uuid_list] - end - - # Link head points to this row, or to the owner of this row (the - # thing to be read) - # - # Link tail originates from this user, or a group that is readable - # by this user (the identity with authorization to read) - # - # Link class is 'permission' ('write' and 'manage' implicitly - # include 'read') - where(sql_conds.join(' OR '), *sql_params) + + # Match any object (evidently a group or user) whose UUID is + # listed explicitly in owner_uuids. + sql_conds += ["#{sql_table}.uuid in (:owner_uuids)"] + + # Match any object whose owner is listed explicitly in + # owner_uuids. + sql_conds += ["#{sql_table}.owner_uuid IN (:owner_uuids)"] + + # Match the head of any permission link whose tail is listed + # explicitly in owner_uuids. + sql_conds += ["#{sql_table}.uuid IN (SELECT head_uuid FROM links WHERE link_class='permission' AND tail_uuid IN (:owner_uuids))"] + + if sql_table == "links" + # Match any permission link that gives one of the authorized + # users some permission _or_ gives anyone else permission to + # view one of the authorized users. + sql_conds += ["(#{sql_table}.link_class in (:permission_link_classes) AND "+ + "(#{sql_table}.head_uuid IN (:user_uuids) OR #{sql_table}.tail_uuid IN (:user_uuids)))"] + end + + where(sql_conds.join(' OR '), + owner_uuids: owner_uuids, + user_uuids: user_uuids, + permission_link_classes: ['permission', 'resources']) end def logged_attributes diff --git a/services/api/app/models/container.rb b/services/api/app/models/container.rb index 4c77008378..1e645e4256 100644 --- a/services/api/app/models/container.rb +++ b/services/api/app/models/container.rb @@ -76,6 +76,20 @@ class Container < ArvadosModel end end + def self.readable_by(*users_list) + if users_list.select { |u| u.is_admin }.any? + return self + end + user_uuids = users_list.map { |u| u.uuid } + uuid_list = user_uuids + users_list.flat_map { |u| u.groups_i_can(:read) } + uuid_list.uniq! + permitted = "(SELECT head_uuid FROM links WHERE link_class='permission' AND tail_uuid IN (:uuids))" + joins(:container_requests). + where("container_requests.uuid IN #{permitted} OR "+ + "container_requests.owner_uuid IN (:uuids)", + uuids: uuid_list) + end + protected def fill_field_defaults diff --git a/services/api/app/models/job.rb b/services/api/app/models/job.rb index a86cc62bf2..e7d1b39ce9 100644 --- a/services/api/app/models/job.rb +++ b/services/api/app/models/job.rb @@ -2,6 +2,7 @@ class Job < ArvadosModel include HasUuid include KindAndEtag include CommonApiTemplate + extend CurrentApiClient serialize :components, Hash attr_protected :arvados_sdk_version, :docker_image_locator serialize :script_parameters, Hash diff --git a/services/api/app/models/log.rb b/services/api/app/models/log.rb index b10a491163..f8d624acb7 100644 --- a/services/api/app/models/log.rb +++ b/services/api/app/models/log.rb @@ -53,6 +53,24 @@ class Log < ArvadosModel self end + def self.readable_by(*users_list) + if users_list.select { |u| u.is_admin }.any? + return self + end + user_uuids = users_list.map { |u| u.uuid } + uuid_list = user_uuids + users_list.flat_map { |u| u.groups_i_can(:read) } + uuid_list.uniq! + permitted = "(SELECT head_uuid FROM links WHERE link_class='permission' AND tail_uuid IN (:uuids))" + joins("LEFT JOIN container_requests ON container_requests.container_uuid=logs.object_uuid"). + where("logs.object_uuid IN #{permitted} OR "+ + "container_requests.uuid IN (:uuids) OR "+ + "container_requests.owner_uuid IN (:uuids) OR "+ + "logs.object_uuid IN (:uuids) OR "+ + "logs.owner_uuid IN (:uuids) OR "+ + "logs.object_owner_uuid IN (:uuids)", + uuids: uuid_list) + end + protected def permission_to_create diff --git a/services/api/lib/eventbus.rb b/services/api/lib/eventbus.rb index e7f2bb1310..16bb030941 100644 --- a/services/api/lib/eventbus.rb +++ b/services/api/lib/eventbus.rb @@ -143,7 +143,7 @@ class EventBus # # Note: find_each implies order('id asc'), which is what we # want. - logs.select(:id).find_each do |l| + logs.select('logs.id').find_each do |l| if not ws.sent_ids.include?(l.id) # only send if not a duplicate ws.send(Log.find(l.id).as_api_response.to_json) diff --git a/services/api/lib/simulate_job_log.rb b/services/api/lib/simulate_job_log.rb index 860513f2f5..0ad543edbf 100644 --- a/services/api/lib/simulate_job_log.rb +++ b/services/api/lib/simulate_job_log.rb @@ -1,4 +1,7 @@ +require 'current_api_client' + module SimulateJobLog + include CurrentApiClient def replay(filename, multiplier = 1, simulated_job_uuid = nil) raise "Environment must be development or test" unless [ 'test', 'development' ].include? ENV['RAILS_ENV'] diff --git a/services/api/test/factories/api_client.rb b/services/api/test/factories/api_client.rb index 7921c35865..78c70fdaac 100644 --- a/services/api/test/factories/api_client.rb +++ b/services/api/test/factories/api_client.rb @@ -2,7 +2,7 @@ FactoryGirl.define do factory :api_client do is_trusted false to_create do |instance| - act_as_system_user do + CurrentApiClientHelper.act_as_system_user do instance.save! end end diff --git a/services/api/test/factories/api_client_authorization.rb b/services/api/test/factories/api_client_authorization.rb index 8bd569e8eb..c3883246eb 100644 --- a/services/api/test/factories/api_client_authorization.rb +++ b/services/api/test/factories/api_client_authorization.rb @@ -11,7 +11,7 @@ FactoryGirl.define do end to_create do |instance| - act_as_user instance.user do + CurrentApiClientHelper.act_as_user instance.user do instance.save! end end diff --git a/services/api/test/factories/user.rb b/services/api/test/factories/user.rb index 56e9125217..6ec9e9f05d 100644 --- a/services/api/test/factories/user.rb +++ b/services/api/test/factories/user.rb @@ -1,4 +1,6 @@ -include CurrentApiClient +class CurrentApiClientHelper + extend CurrentApiClient +end FactoryGirl.define do factory :user do @@ -6,7 +8,7 @@ FactoryGirl.define do join_groups [] end after :create do |user, evaluator| - act_as_system_user do + CurrentApiClientHelper.act_as_system_user do evaluator.join_groups.each do |g| Link.create!(tail_uuid: user.uuid, head_uuid: g.uuid, @@ -27,7 +29,7 @@ FactoryGirl.define do factory :active_user do is_active true after :create do |user| - act_as_system_user do + CurrentApiClientHelper.act_as_system_user do Link.create!(tail_uuid: user.uuid, head_uuid: Group.where('uuid ~ ?', '-f+$').first.uuid, link_class: 'permission', @@ -36,7 +38,7 @@ FactoryGirl.define do end end to_create do |instance| - act_as_system_user do + CurrentApiClientHelper.act_as_system_user do instance.save! end end diff --git a/services/api/test/fixtures/container_requests.yml b/services/api/test/fixtures/container_requests.yml index 433aff210a..dcaf657d42 100644 --- a/services/api/test/fixtures/container_requests.yml +++ b/services/api/test/fixtures/container_requests.yml @@ -36,7 +36,7 @@ running: vcpus: 1 ram: 123 -running-older: +running_older: uuid: zzzzz-xvhdp-cr4runningcntn2 owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz name: running @@ -93,6 +93,25 @@ completed-older: vcpus: 1 ram: 123 +requester: + uuid: zzzzz-xvhdp-9zacv3o1xw6sxz5 + owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz + name: requester + state: Committed + priority: 1 + created_at: 2016-01-11 11:11:11.111111111 Z + updated_at: 2016-01-11 11:11:11.111111111 Z + modified_at: 2016-01-11 11:11:11.111111111 Z + modified_by_user_uuid: zzzzz-tpzed-xurymjxw79nv3jz + container_image: test + cwd: / + output_path: /output + command: ["request-another-container", "echo", "hello"] + container_uuid: zzzzz-dz642-requestingcntnr + runtime_constraints: + vcpus: 1 + ram: 123 + cr_for_requester: uuid: zzzzz-xvhdp-cr4requestercnt owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz diff --git a/services/api/test/fixtures/containers.yml b/services/api/test/fixtures/containers.yml index 049cd3c6db..2e3021d374 100644 --- a/services/api/test/fixtures/containers.yml +++ b/services/api/test/fixtures/containers.yml @@ -1,6 +1,6 @@ queued: uuid: zzzzz-dz642-queuedcontainer - owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz + owner_uuid: zzzzz-tpzed-000000000000000 state: Queued priority: 1 created_at: 2016-01-11 11:11:11.111111111 Z @@ -16,8 +16,7 @@ queued: running: uuid: zzzzz-dz642-runningcontainr - owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz - state: Running + owner_uuid: zzzzz-tpzed-000000000000000 priority: 1 created_at: <%= 1.minute.ago.to_s(:db) %> updated_at: <%= 1.minute.ago.to_s(:db) %> @@ -32,9 +31,9 @@ running: vcpus: 4 auth_uuid: zzzzz-gj3su-077z32aux8dg2s1 -running-older: +running_older: uuid: zzzzz-dz642-runningcontain2 - owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz + owner_uuid: zzzzz-tpzed-000000000000000 state: Running priority: 1 created_at: <%= 2.minute.ago.to_s(:db) %> @@ -51,7 +50,7 @@ running-older: locked: uuid: zzzzz-dz642-lockedcontainer - owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz + owner_uuid: zzzzz-tpzed-000000000000000 state: Locked priority: 2 created_at: <%= 2.minute.ago.to_s(:db) %> @@ -67,7 +66,7 @@ locked: completed: uuid: zzzzz-dz642-compltcontainer - owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz + owner_uuid: zzzzz-tpzed-000000000000000 state: Complete exit_code: 0 priority: 1 @@ -87,7 +86,7 @@ completed: completed_older: uuid: zzzzz-dz642-compltcontainr2 - owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz + owner_uuid: zzzzz-tpzed-000000000000000 state: Complete exit_code: 0 priority: 1 @@ -106,7 +105,7 @@ completed_older: requester: uuid: zzzzz-dz642-requestingcntnr - owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz + owner_uuid: zzzzz-tpzed-000000000000000 state: Complete exit_code: 0 priority: 1 @@ -123,7 +122,7 @@ requester: requester_container: uuid: zzzzz-dz642-requestercntnr1 - owner_uuid: zzzzz-tpzed-xurymjxw79nv3jz + owner_uuid: zzzzz-tpzed-000000000000000 state: Complete exit_code: 0 priority: 1 diff --git a/services/api/test/fixtures/logs.yml b/services/api/test/fixtures/logs.yml index 9179e6dff9..2f45d69fd2 100644 --- a/services/api/test/fixtures/logs.yml +++ b/services/api/test/fixtures/logs.yml @@ -1,7 +1,9 @@ -noop: +noop: # nothing happened ...to the 'spectator' user id: 1 uuid: zzzzz-xxxxx-pshmckwoma9plh7 + owner_uuid: zzzzz-tpzed-000000000000000 object_uuid: zzzzz-tpzed-l1s2piq4t4mps8r + object_owner_uuid: zzzzz-tpzed-000000000000000 event_at: <%= 1.minute.ago.to_s(:db) %> admin_changes_repository2: # admin changes repository2, which is owned by active user diff --git a/services/api/test/integration/permissions_test.rb b/services/api/test/integration/permissions_test.rb index 44b5e6e377..e4db862415 100644 --- a/services/api/test/integration/permissions_test.rb +++ b/services/api/test/integration/permissions_test.rb @@ -1,9 +1,14 @@ require 'test_helper' class PermissionsTest < ActionDispatch::IntegrationTest + include DbCurrentTime include CurrentApiClient # for empty_collection fixtures :users, :groups, :api_client_authorizations, :collections + teardown do + User.invalidate_permissions_cache db_current_time.to_i + end + test "adding and removing direct can_read links" do # try to read collection as spectator get "/arvados/v1/collections/#{collections(:foo_file).uuid}", {:format => :json}, auth(:spectator) @@ -341,11 +346,6 @@ class PermissionsTest < ActionDispatch::IntegrationTest assert_response 404 end - test "get_permissions returns 404 for unreadable uuid" do - get "/arvados/v1/permissions/#{groups(:public).uuid}", nil, auth(:active) - assert_response 404 - end - test "get_permissions returns 403 if user can read but not manage" do post "/arvados/v1/links", { :link => { diff --git a/services/api/test/integration/websocket_test.rb b/services/api/test/integration/websocket_test.rb index 0c99fcc4e6..99ca7ac960 100644 --- a/services/api/test/integration/websocket_test.rb +++ b/services/api/test/integration/websocket_test.rb @@ -69,7 +69,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest test "connect, subscribe and get response" do status = nil - ws_helper :admin do |ws| + ws_helper :active do |ws| ws.on :open do |event| ws.send ({method: 'subscribe'}.to_json) end @@ -89,9 +89,9 @@ class WebsocketTest < ActionDispatch::IntegrationTest spec = nil ev_uuid = nil - authorize_with :admin + authorize_with :active - ws_helper :admin do |ws| + ws_helper :active do |ws| ws.on :open do |event| ws.send ({method: 'subscribe'}.to_json) end @@ -126,9 +126,9 @@ class WebsocketTest < ActionDispatch::IntegrationTest spec_ev_uuid = nil human_ev_uuid = nil - authorize_with :admin + authorize_with :active - ws_helper :admin do |ws| + ws_helper :active do |ws| ws.on :open do |event| ws.send ({method: 'subscribe'}.to_json) end @@ -166,9 +166,9 @@ class WebsocketTest < ActionDispatch::IntegrationTest human = nil human_ev_uuid = nil - authorize_with :admin + authorize_with :active - ws_helper :admin do |ws| + ws_helper :active do |ws| ws.on :open do |event| ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json) end @@ -204,9 +204,9 @@ class WebsocketTest < ActionDispatch::IntegrationTest spec_ev_uuid = nil human_ev_uuid = nil - authorize_with :admin + authorize_with :active - ws_helper :admin do |ws| + ws_helper :active do |ws| ws.on :open do |event| ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json) ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#specimen']]}.to_json) @@ -249,9 +249,9 @@ class WebsocketTest < ActionDispatch::IntegrationTest state = 1 t1 = nil - authorize_with :admin + authorize_with :active - ws_helper :admin do |ws| + ws_helper :active do |ws| ws.on :open do |event| ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#trait'], ['event_type', '=', 'update']]}.to_json) end @@ -285,13 +285,13 @@ class WebsocketTest < ActionDispatch::IntegrationTest human = nil human_ev_uuid = nil - authorize_with :admin + authorize_with :active lastid = logs(:admin_changes_specimen).id l1 = nil l2 = nil - ws_helper :admin do |ws| + ws_helper :active do |ws| ws.on :open do |event| ws.send ({method: 'subscribe', last_log_id: lastid}.to_json) end @@ -329,9 +329,9 @@ class WebsocketTest < ActionDispatch::IntegrationTest spec_ev_uuid = nil filter_id = nil - authorize_with :admin + authorize_with :active - ws_helper :admin, false do |ws| + ws_helper :active, false do |ws| ws.on :open do |event| ws.send ({method: 'subscribe'}.to_json) EM::Timer.new 3 do @@ -378,9 +378,9 @@ class WebsocketTest < ActionDispatch::IntegrationTest spec = nil spec_ev_uuid = nil - authorize_with :admin + authorize_with :active - ws_helper :admin, false do |ws| + ws_helper :active, false do |ws| ws.on :open do |event| ws.send ({method: 'subscribe', filters: [['object_uuid', 'is_a', 'arvados#human']]}.to_json) EM::Timer.new 6 do @@ -430,9 +430,9 @@ class WebsocketTest < ActionDispatch::IntegrationTest human = nil human_ev_uuid = nil - authorize_with :admin + authorize_with :active - ws_helper :admin do |ws| + ws_helper :active do |ws| ws.on :open do |event| ws.send ({method: 'subscribe'}.to_json) end @@ -477,9 +477,9 @@ class WebsocketTest < ActionDispatch::IntegrationTest test "connected, not subscribed, no event" do slow_test - authorize_with :admin + authorize_with :active - ws_helper :admin, false do |ws| + ws_helper :active, false do |ws| ws.on :open do |event| EM::Timer.new 1 do Specimen.create @@ -530,7 +530,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest test "connect, try bogus method" do status = nil - ws_helper :admin do |ws| + ws_helper :active do |ws| ws.on :open do |event| ws.send ({method: 'frobnabble'}.to_json) end @@ -548,7 +548,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest test "connect, missing method" do status = nil - ws_helper :admin do |ws| + ws_helper :active do |ws| ws.on :open do |event| ws.send ({fizzbuzz: 'frobnabble'}.to_json) end @@ -566,7 +566,7 @@ class WebsocketTest < ActionDispatch::IntegrationTest test "connect, send malformed request" do status = nil - ws_helper :admin do |ws| + ws_helper :active do |ws| ws.on :open do |event| ws.send '' end @@ -585,9 +585,9 @@ class WebsocketTest < ActionDispatch::IntegrationTest test "connect, try subscribe too many filters" do state = 1 - authorize_with :admin + authorize_with :active - ws_helper :admin do |ws| + ws_helper :active do |ws| ws.on :open do |event| (1..17).each do |i| ws.send ({method: 'subscribe', filters: [['object_uuid', '=', i]]}.to_json) @@ -618,9 +618,9 @@ class WebsocketTest < ActionDispatch::IntegrationTest event_count = 0 log_start = Log.order(:id).last.id - authorize_with :admin + authorize_with :active - ws_helper :admin, false do |ws| + ws_helper :active, false do |ws| EM::Timer.new 45 do # Needs a longer timeout than the default ws.close @@ -661,9 +661,9 @@ class WebsocketTest < ActionDispatch::IntegrationTest human = nil human_ev_uuid = nil - authorize_with :admin + authorize_with :active - ws_helper :admin do |ws| + ws_helper :active do |ws| ws.on :open do |event| # test that #6451 is fixed (invalid filter crashes websockets) ws.send ({method: 'subscribe', filters: [['object_blarg', 'is_a', 'arvados#human']]}.to_json) diff --git a/services/api/test/test_helper.rb b/services/api/test/test_helper.rb index ef08c726ae..417ddf6bee 100644 --- a/services/api/test/test_helper.rb +++ b/services/api/test/test_helper.rb @@ -47,6 +47,7 @@ class ActiveSupport::TestCase fixtures :all include ArvadosTestSupport + include CurrentApiClient setup do Rails.logger.warn "\n\n#{'=' * 70}\n#{self.class}\##{method_name}\n#{'-' * 70}\n\n" diff --git a/services/api/test/unit/container_request_test.rb b/services/api/test/unit/container_request_test.rb index 3ab4a891f7..ebd6a5a19a 100644 --- a/services/api/test/unit/container_request_test.rb +++ b/services/api/test/unit/container_request_test.rb @@ -389,4 +389,10 @@ class ContainerRequestTest < ActiveSupport::TestCase end end end + + test "requestor can retrieve container owned by dispatch" do + assert_not_empty Container.readable_by(users(:admin)).where(uuid: containers(:running).uuid) + assert_not_empty Container.readable_by(users(:active)).where(uuid: containers(:running).uuid) + assert_empty Container.readable_by(users(:spectator)).where(uuid: containers(:running).uuid) + end end diff --git a/services/api/test/unit/log_test.rb b/services/api/test/unit/log_test.rb index fd71576dfe..632271e98c 100644 --- a/services/api/test/unit/log_test.rb +++ b/services/api/test/unit/log_test.rb @@ -253,7 +253,8 @@ class LogTest < ActiveSupport::TestCase :crunchstat_for_running_job] # log & job owned by active c = Log.readable_by(users(:spectator)).order("id asc").each.to_a - assert_log_result c, known_logs, [:admin_changes_specimen, # owned by spectator + assert_log_result c, known_logs, [:noop, # object_uuid is spectator + :admin_changes_specimen, # object_uuid is a specimen owned by spectator :system_adds_baz] # readable via 'all users' group end diff --git a/services/api/test/unit/permission_test.rb b/services/api/test/unit/permission_test.rb index 4a6ddc69fb..79fc1f29c7 100644 --- a/services/api/test/unit/permission_test.rb +++ b/services/api/test/unit/permission_test.rb @@ -353,4 +353,27 @@ class PermissionTest < ActiveSupport::TestCase ob.update_attributes!(owner_uuid: groups(:aproject).uuid) end end + + def container_logs(container, user) + Log.readable_by(users(user)). + where(object_uuid: containers(container).uuid, event_type: "test") + end + + test "container logs created by dispatch are visible to container requestor" do + set_user_from_auth :dispatch1 + Log.create!(object_uuid: containers(:running).uuid, + event_type: "test") + + assert_not_empty container_logs(:running, :admin) + assert_not_empty container_logs(:running, :active) + assert_empty container_logs(:running, :spectator) + end + + test "container logs created by dispatch are public if container request is public" do + set_user_from_auth :dispatch1 + Log.create!(object_uuid: containers(:running_older).uuid, + event_type: "test") + + assert_not_empty container_logs(:running_older, :anonymous) + end end