Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add a smart attribute for automatically loading modules #1930

Merged
merged 7 commits into from Apr 7, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/dashboard/app/models/hpc_module.rb
Expand Up @@ -23,6 +23,12 @@ def all(cluster)
end
end
end

def all_versions(module_name)
Configuration.job_clusters.map do |cluster|
all(cluster.id).select { |m| m.name == module_name.to_s }
end.flatten.uniq(&:to_s).sort_by(&:version).reverse
end
end

attr_reader :name, :version
Expand All @@ -39,4 +45,12 @@ def to_s
def default?
version.nil?
end

def ==(other)
johrstrom marked this conversation as resolved.
Show resolved Hide resolved
to_s == other.to_s
end

def on_cluster?(cluster_name)
HpcModule.all(cluster_name).any? { |m| m == self }
end
end
9 changes: 9 additions & 0 deletions apps/dashboard/config/configuration_singleton.rb
Expand Up @@ -89,6 +89,15 @@ def login_clusters
)
end

# clusters you can submit jobs to
def job_clusters
@job_clusters ||= OodCore::Clusters.new(
OodAppkit.clusters
.select(&:job_allow?)
.reject { |c| c.metadata.hidden }
)
end

# @return [String, nil] version string from VERSION file, or nil if no file avail
def version_from_file(dir)
file = Pathname.new(dir).join("VERSION")
Expand Down
7 changes: 7 additions & 0 deletions apps/dashboard/lib/smart_attributes/attribute_factory.rb
Expand Up @@ -2,13 +2,20 @@ module SmartAttributes
class AttributeFactory
extend ActiveSupport::Autoload

AUTO_MODULES_REX = /^auto_modules_([\w_]+)$/.freeze
johrstrom marked this conversation as resolved.
Show resolved Hide resolved

class << self
# Build an attribute object from an id and its options
# @param id [#to_s] id of attribute
# @param opts [Hash] attribute options
# @return [Attribute] the attribute object
def build(id, opts = {})
id = id.to_s
if id.match?(AUTO_MODULES_REX)
hpc_mod = id.match(AUTO_MODULES_REX)[1]
id = 'auto_modules'
opts = opts.merge({'module' => hpc_mod})
end

path_to_attribute = "smart_attributes/attributes/#{id}"
begin
Expand Down
46 changes: 46 additions & 0 deletions apps/dashboard/lib/smart_attributes/attributes/auto_modules.rb
@@ -0,0 +1,46 @@
# frozen_string_literal: true

module SmartAttributes
class AttributeFactory
# Build this attribute object with defined options
# @param opts [Hash] attribute's options
# @return [Attributes::AutoModules] the attribute object
def self.build_auto_modules(opts = {})
Attributes::AutoModules.new('auto_modules', opts)
end
end

module Attributes
# AutoModules populates a select widget of modules from HpcModule class
# that is cluster aware. Meaning it will attach data-option-for-cluster-X
# attributes to the options.
class AutoModules < Attribute
def initialize(id, opts = {})
johrstrom marked this conversation as resolved.
Show resolved Hide resolved
super

@hpc_module = @opts[:module]
end

def widget
'select'
end

# Form label for this attribute
# @param fmt [String, nil] formatting of form label
# @return [String] form label
def label(*)
(opts[:label] || 'Module Version').to_s
end

def select_choices
HpcModule.all_versions(@hpc_module).map do |mod|
data_opts = Configuration.job_clusters.map do |cluster|
{ "data-option-for-cluster-#{cluster.id}": false } unless mod.on_cluster?(cluster.id)
end.compact

[mod.version, mod.version].concat(data_opts)
end
end
end
end
end
1 change: 1 addition & 0 deletions apps/dashboard/test/fixtures/modules/oakley.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions apps/dashboard/test/fixtures/modules/owens.json

Large diffs are not rendered by default.

15 changes: 0 additions & 15 deletions apps/dashboard/test/fixtures/modules/simple.json

This file was deleted.

Expand Up @@ -140,3 +140,4 @@ form:
- classroom
- classroom_size
- advanced_options
- auto_modules_app_jupyter
35 changes: 31 additions & 4 deletions apps/dashboard/test/models/hpc_module_test.rb
Expand Up @@ -3,6 +3,13 @@
require 'test_helper'

class HpcModuleTest < ActiveSupport::TestCase

# owens.json and oakley.json in this directory are from real clusters
# (oakley is actually pitzer) from the time this was written.
def fixture_dir
"#{Rails.root}/test/fixtures/modules/"
end

test 'all safely reads from inaccessabile directories' do
with_modified_env({ OOD_MODULE_FILE_DIR: '/dev/null' }) do
assert_equal [], HpcModule.all('owens')
Expand All @@ -18,10 +25,30 @@ class HpcModuleTest < ActiveSupport::TestCase
end
end

test 'reads a simple file' do
with_modified_env({ OOD_MODULE_FILE_DIR: "#{Rails.root}/test/fixtures/modules/" }) do
# NOTE: that there are no duplicates and rstudio has no version
assert_equal(['jupyter/1', 'jupyter/2', 'rstudio'], HpcModule.all('simple').map(&:to_s))
test 'all versions is corrrect, sorted and unique' do
stub_sys_apps
with_modified_env({ OOD_MODULE_FILE_DIR: fixture_dir }) do
expected = [
'app_jupyter/3.1.18', 'app_jupyter/3.0.17', 'app_jupyter/2.3.2', 'app_jupyter/2.2.10',
'app_jupyter/1.2.21', 'app_jupyter/1.2.16', 'app_jupyter/0.35.6'
]
assert_equal(expected, HpcModule.all_versions('app_jupyter').map(&:to_s))
end
end

test 'all versions returns empty array when it cant find' do
stub_sys_apps
with_modified_env({ OOD_MODULE_FILE_DIR: fixture_dir }) do
assert_equal([], HpcModule.all_versions('wont_find').map(&:to_s))
assert_equal([], HpcModule.all_versions(nil).map(&:to_s))
end
end

test 'on_cluster? can find modules' do
stub_sys_apps
with_modified_env({ OOD_MODULE_FILE_DIR: fixture_dir }) do
assert HpcModule.new('app_jupyter', version: '0.35.6').on_cluster?('oakley')
assert !HpcModule.new('app_jupyter', version: '0.35.6').on_cluster?('owens')
end
end

Expand Down
21 changes: 21 additions & 0 deletions apps/dashboard/test/system/batch_connect_test.rb
Expand Up @@ -448,4 +448,25 @@ def setup
click_on('Launch')
verify_bc_alert('sys/bc_jupyter', 'save', err_msg)
end

test 'auto generated modules are dynamic' do
with_modified_env({ OOD_MODULE_FILE_DIR: 'test/fixtures/modules' }) do
visit new_batch_connect_session_context_url('sys/bc_jupyter')

# defaults
assert_equal '3.0.17', find_value('auto_modules')
assert_equal 'owens', find_value('cluster')
# versions not available on owens
assert_equal 'display: none;', find_option_style('auto_modules', '3.1.18')
assert_equal 'display: none;', find_option_style('auto_modules', '1.2.16')
assert_equal 'display: none;', find_option_style('auto_modules', '0.35.6')

# select oakley and now they're available
select('oakley', from: bc_ele_id('cluster'))
assert_equal '3.0.17', find_value('auto_modules')
assert_equal '', find_option_style('auto_modules', '3.1.18')
assert_equal '', find_option_style('auto_modules', '1.2.16')
assert_equal '', find_option_style('auto_modules', '0.35.6')
end
end
end