Skip to content

Commit 5424c33

Browse files
committed
Disable workflows when scheduler doesn’t support them (#5395)
1 parent 284dae5 commit 5424c33

9 files changed

Lines changed: 113 additions & 2 deletions

File tree

apps/dashboard/app/controllers/application_controller.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ class ApplicationController < ActionController::Base
99
before_action :set_featured_group, :set_custom_navigation
1010
before_action :check_required_announcements
1111

12+
helper_method :workflows_supported?
13+
14+
def workflows_supported?
15+
@workflows_supported ||= Workflow.supported?
16+
end
17+
1218
def check_required_announcements
1319
return if instance_of?(SettingsController)
1420

apps/dashboard/app/controllers/projects_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def show
1515
end
1616
else
1717
@launchers = Launcher.all(@project.directory)
18-
@workflows = Workflow.all(@project.directory)
18+
@workflows = workflows_supported? ? Workflow.all(@project.directory) : []
1919
@valid_project = Launcher.clusters?
2020
@valid_scripts = Launcher.scripts?(@project.directory)
2121

apps/dashboard/app/controllers/workflows_controller.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
class WorkflowsController < ApplicationController
55
wrap_parameters false
66

7+
before_action :ensure_workflows_supported!
8+
79
# GET /projects/:id/workflows/:id
810
def show
911
return unless load_project_and_workflow_objects
@@ -120,6 +122,17 @@ def submit
120122

121123
private
122124

125+
def ensure_workflows_supported!
126+
return if workflows_supported?
127+
128+
message = I18n.t('dashboard.jobs_workflows_not_supported')
129+
130+
respond_to do |format|
131+
format.html { redirect_to(project_path(params[:project_id]), alert: message) }
132+
format.json { render json: { message: message }, status: :not_implemented }
133+
end
134+
end
135+
123136
def load_project_and_workflow_objects(render_json: false)
124137
@project = Project.find(project_id)
125138
@workflow = Workflow.find(workflow_id, project_directory)

apps/dashboard/app/models/launcher.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ def scripts?(project_dir)
5555
script_attribute = SmartAttributes::AttributeFactory.build('auto_scripts', { directory: project_dir })
5656
script_attribute.select_choices(hide_excludable: false).any?
5757
end
58+
59+
def workflows_supported?
60+
Workflow.supported?
61+
end
5862
end
5963

6064
ID_REX = /\A\w{8}\Z/.freeze

apps/dashboard/app/models/workflow.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,45 @@ class Workflow
55
include ProjectPermissions
66

77
class << self
8+
def supported?
9+
dependency_keys = %i[after afterok afternotok afterany].freeze
10+
11+
clusters_obj = OodAppkit.clusters
12+
clusters =
13+
if clusters_obj.respond_to?(:values)
14+
Array(clusters_obj.values)
15+
elsif clusters_obj.respond_to?(:to_a)
16+
Array(clusters_obj.to_a)
17+
elsif clusters_obj.respond_to?(:each)
18+
clusters_obj.each.to_a
19+
else
20+
[]
21+
end.compact
22+
return false if clusters.empty?
23+
24+
clusters.any? do |cluster|
25+
job_adapter =
26+
begin
27+
cluster.respond_to?(:job_adapter) ? cluster.job_adapter : nil
28+
rescue OodCore::AdapterNotSpecified => e
29+
Rails.logger.debug("Workflow.supported?: adapter not specified for cluster: #{e.message}")
30+
nil
31+
end
32+
next false if job_adapter.nil?
33+
34+
method = job_adapter.method(:submit)
35+
params = method.parameters
36+
37+
next true if params.any? { |type, _name| type == :keyrest }
38+
39+
accepted_keys = params.select { |type, _name| type == :key || type == :keyreq }.map { |_type, name| name }.compact.map(&:to_sym)
40+
41+
(dependency_keys - accepted_keys).empty?
42+
rescue NameError, NoMethodError
43+
false
44+
end
45+
end
46+
847
def workflow_dir(project_dir)
948
Pathname.new("#{project_dir}/.ondemand/workflows")
1049
end

apps/dashboard/app/views/projects/show.html.erb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@
5252
<%= t('dashboard.jobs_new_launcher') %>
5353
</a>
5454
</div>
55-
<%= render partial: 'workflows/workflow', locals: { project: @project, workflows: @workflows } %>
55+
<% if workflows_supported? %>
56+
<%= render partial: 'workflows/workflow', locals: { project: @project, workflows: @workflows } %>
57+
<% end %>
5658
</div>
5759

5860
<div id="project_jobs_files_col" class="row col-sm-8 col-lg-10 d-flex">

apps/dashboard/config/locales/en.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ en:
99
used: "%{attribute} is already used"
1010
dashboard:
1111
active_jobs_close_details: "Close extended details panel."
12+
jobs_workflows_not_supported: "Workflows are not supported by the configured scheduler."
1213
active_sessions_caption_html: <a href="%{all_sessions_url}"> view all (%{number_of_sessions})</a>
1314
active_sessions_title: Active interactive sessions
1415
add: Add

apps/dashboard/test/models/launcher_test.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,31 @@ def setup
157157
refute(Dir.exist?(Launcher.launchers_dir(tmp).to_s))
158158
end
159159
end
160+
161+
class PositionalHashSubmitAdapter
162+
def submit(_script, _opts = {}); end
163+
end
164+
165+
class KeywordSubmitAdapter
166+
def submit(_script, after: [], afterok: [], afternotok: [], afterany: []); end
167+
end
168+
169+
class KeyrestSubmitAdapter
170+
def submit(_script, **_kwargs); end
171+
end
172+
173+
test 'workflows_supported? returns false when submit does not accept keywords' do
174+
OodAppkit.stubs(:clusters).returns({ test: OpenStruct.new(job_adapter: PositionalHashSubmitAdapter.new) })
175+
assert_equal false, Launcher.workflows_supported?
176+
end
177+
178+
test 'workflows_supported? returns true when submit accepts dependency keywords' do
179+
OodAppkit.stubs(:clusters).returns({ test: OpenStruct.new(job_adapter: KeywordSubmitAdapter.new) })
180+
assert_equal true, Launcher.workflows_supported?
181+
end
182+
183+
test 'workflows_supported? returns true when submit accepts arbitrary keywords' do
184+
OodAppkit.stubs(:clusters).returns({ test: OpenStruct.new(job_adapter: KeyrestSubmitAdapter.new) })
185+
assert_equal true, Launcher.workflows_supported?
186+
end
160187
end

apps/dashboard/test/system/project_manager_test.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,25 @@ def check_directory_breakpoints
11091109
end
11101110
end
11111111

1112+
test 'workflows are hidden and disabled when unsupported' do
1113+
Workflow.singleton_class.alias_method(:_supported_orig_for_test, :supported?)
1114+
Workflow.define_singleton_method(:supported?) { false }
1115+
1116+
Dir.mktmpdir do |dir|
1117+
project_id = setup_project(dir)
1118+
1119+
visit project_path(project_id)
1120+
assert_no_text(I18n.t('dashboard.jobs_workflows'))
1121+
1122+
visit new_project_workflow_path(project_id)
1123+
assert_current_path(project_path(project_id))
1124+
assert_selector('.alert-danger', text: I18n.t('dashboard.jobs_workflows_not_supported'))
1125+
end
1126+
ensure
1127+
Workflow.singleton_class.alias_method(:supported?, :_supported_orig_for_test)
1128+
Workflow.singleton_class.remove_method(:_supported_orig_for_test)
1129+
end
1130+
11121131
# this test:
11131132
# creates a project & launcher with auto_accounts
11141133
# excludes some of the accounts from auto_accounts in launcher#edit

0 commit comments

Comments
 (0)