ood_core 0.28.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/ood_core/cluster.rb +11 -1
- data/lib/ood_core/job/account_info.rb +0 -9
- data/lib/ood_core/job/adapter.rb +2 -1
- data/lib/ood_core/job/adapters/coder/batch.rb +46 -25
- data/lib/ood_core/job/adapters/coder/credentials.rb +16 -0
- data/lib/ood_core/job/adapters/coder/openstack_credentials.rb +118 -0
- data/lib/ood_core/job/adapters/coder.rb +10 -2
- data/lib/ood_core/job/adapters/htcondor.rb +549 -0
- data/lib/ood_core/job/adapters/linux_host/templates/script_wrapper.erb.sh +1 -1
- data/lib/ood_core/job/adapters/psij/delete.py +18 -0
- data/lib/ood_core/job/adapters/psij/get_info.py +55 -0
- data/lib/ood_core/job/adapters/psij/hold.py +18 -0
- data/lib/ood_core/job/adapters/psij/release.py +18 -0
- data/lib/ood_core/job/adapters/psij/submit.py +28 -0
- data/lib/ood_core/job/adapters/psij.rb +410 -0
- data/lib/ood_core/job/adapters/sge/batch.rb +13 -10
- data/lib/ood_core/job/adapters/slurm.rb +39 -11
- data/lib/ood_core/job/adapters/systemd/templates/script_wrapper.erb.sh +1 -1
- data/lib/ood_core/job/queue_info.rb +8 -2
- data/lib/ood_core/version.rb +1 -1
- data/ood_core.gemspec +1 -0
- metadata +25 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 14dedae6a9b9508f87bb9b950520fb4614f345a90d8841c23836c2650a5b5d2f
|
|
4
|
+
data.tar.gz: 357f6a0eec8b1029f6ad1d32983a55e071b6fda90b453953a8400c5e7f442c68
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0c7782f857836971b0f26207669c9ef21746b0490755a0e86ba0eea6d4b75069c61e7a5c349aea813b4c38f812e099725d97ba0b7d8e916a3cd00e859e12e4eb
|
|
7
|
+
data.tar.gz: 798c9a6709dbba8d704e51942e9677ca632df7c773689dafeaebbe2212f560c2a18643f15dfa8550bdc1bff810eda34b929ce9fd4d84fef10001a68d63533056
|
data/lib/ood_core/cluster.rb
CHANGED
|
@@ -77,6 +77,16 @@ module OodCore
|
|
|
77
77
|
end
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
def title
|
|
81
|
+
if !metadata.title.nil?
|
|
82
|
+
metadata.title
|
|
83
|
+
elsif id.to_s.respond_to?(:titleize)
|
|
84
|
+
id.to_s.titleize
|
|
85
|
+
else
|
|
86
|
+
id.to_s
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
80
90
|
# Metadata that provides extra information about this cluster
|
|
81
91
|
# @return [OpenStruct] the metadata
|
|
82
92
|
def metadata
|
|
@@ -100,7 +110,7 @@ module OodCore
|
|
|
100
110
|
# Build a job adapter from the job configuration
|
|
101
111
|
# @return [Job::Adapter] the job adapter
|
|
102
112
|
def job_adapter
|
|
103
|
-
Job::Factory.build(job_config)
|
|
113
|
+
Job::Factory.build(job_config.merge({ id: id }))
|
|
104
114
|
end
|
|
105
115
|
|
|
106
116
|
# Whether the job feature is allowed based on the ACLs
|
|
@@ -12,19 +12,10 @@ module OodCore
|
|
|
12
12
|
# The QoS values this account can use.
|
|
13
13
|
attr_reader :qos
|
|
14
14
|
|
|
15
|
-
# The cluster this account is associated with.
|
|
16
|
-
attr_reader :cluster
|
|
17
|
-
|
|
18
|
-
# The queue this account can use. nil means there is no queue info
|
|
19
|
-
# for this account.
|
|
20
|
-
attr_reader :queue
|
|
21
|
-
|
|
22
15
|
def initialize(**opts)
|
|
23
16
|
orig_name = opts.fetch(:name, 'unknown')
|
|
24
17
|
@name = upcase_accounts? ? orig_name.upcase : orig_name
|
|
25
18
|
@qos = opts.fetch(:qos, [])
|
|
26
|
-
@cluster = opts.fetch(:cluster, nil)
|
|
27
|
-
@queue = opts.fetch(:queue, nil)
|
|
28
19
|
end
|
|
29
20
|
|
|
30
21
|
def to_h
|
data/lib/ood_core/job/adapter.rb
CHANGED
|
@@ -210,7 +210,8 @@ module OodCore
|
|
|
210
210
|
ENV["OOD_JOB_NAME_ILLEGAL_CHARS"].to_s
|
|
211
211
|
end
|
|
212
212
|
|
|
213
|
-
# Retrieve the accounts available to use
|
|
213
|
+
# Retrieve the accounts available to use for the current user.
|
|
214
|
+
# The same account might appear muiltiple times if it has access to multiple clusters.
|
|
214
215
|
#
|
|
215
216
|
# Subclasses that do not implement this will return empty arrays.
|
|
216
217
|
# @return [Array<AccountInfo>] the accounts available to the user.
|
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
require "ood_core/refinements/hash_extensions"
|
|
2
2
|
require "json"
|
|
3
|
+
|
|
3
4
|
|
|
4
5
|
# Utility class for the Coder adapter to interact with the Coders API.
|
|
5
6
|
class OodCore::Job::Adapters::Coder::Batch
|
|
6
7
|
require_relative "coder_job_info"
|
|
7
8
|
class Error < StandardError; end
|
|
8
|
-
def initialize(config)
|
|
9
|
+
def initialize(config, credentials)
|
|
9
10
|
@host = config[:host]
|
|
10
11
|
@token = config[:token]
|
|
12
|
+
@service_user = config[:service_user]
|
|
13
|
+
@credential_deletion_max_attempts = config[:credential_deletion_max_attempts] || 5
|
|
14
|
+
@credential_deletion_timeout_interval = config[:credential_deletion_timeout_interval] || 10
|
|
15
|
+
@credentials = credentials
|
|
11
16
|
end
|
|
12
17
|
|
|
13
|
-
def
|
|
14
|
-
credentials_file = File.read("/home/#{username}/application_credentials.json")
|
|
15
|
-
credentials = JSON.parse(credentials_file)
|
|
16
|
-
credentials.find { |cred| cred["project_id"] == project_id }
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def get_rich_parameters(coder_parameters, project_id, os_app_credentials)
|
|
18
|
+
def get_rich_parameters(coder_parameters, project_id, app_credentials)
|
|
20
19
|
rich_parameter_values = [
|
|
21
|
-
{ name: "application_credential_name", value:
|
|
22
|
-
{ name: "application_credential_id", value:
|
|
23
|
-
{ name: "application_credential_secret", value:
|
|
20
|
+
{ name: "application_credential_name", value: app_credentials[:name] },
|
|
21
|
+
{ name: "application_credential_id", value: app_credentials[:id] },
|
|
22
|
+
{ name: "application_credential_secret", value: app_credentials[:secret] },
|
|
24
23
|
{name: "project_id", value: project_id }
|
|
25
24
|
]
|
|
26
25
|
if coder_parameters
|
|
@@ -43,34 +42,59 @@ class OodCore::Job::Adapters::Coder::Batch
|
|
|
43
42
|
org_id = script.native[:org_id]
|
|
44
43
|
project_id = script.native[:project_id]
|
|
45
44
|
coder_parameters = script.native[:coder_parameters]
|
|
46
|
-
endpoint = "
|
|
47
|
-
|
|
45
|
+
endpoint = "#{@host}/api/v2/organizations/#{org_id}/members/#{@service_user}/workspaces"
|
|
46
|
+
app_credentials = @credentials.generate_credentials(project_id, username)
|
|
48
47
|
headers = get_headers(@token)
|
|
48
|
+
workspace_name = "#{username}-#{script.native[:workspace_name]}-#{rand(2_821_109_907_456).to_s(36)}"
|
|
49
49
|
body = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
rich_parameter_values: get_rich_parameters(coder_parameters, project_id, os_app_credentials),
|
|
50
|
+
template_version_id: script.native[:template_version_id],
|
|
51
|
+
name: workspace_name,
|
|
52
|
+
rich_parameter_values: get_rich_parameters(coder_parameters, project_id, app_credentials),
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
resp = api_call('post', endpoint, headers, body)
|
|
56
|
+
@credentials.save_credentials(resp["id"], username, app_credentials)
|
|
57
57
|
resp["id"]
|
|
58
|
+
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
def delete(id)
|
|
61
|
-
endpoint = "
|
|
62
|
+
endpoint = "#{@host}/api/v2/workspaces/#{id}/builds"
|
|
62
63
|
headers = get_headers(@token)
|
|
63
64
|
body = {
|
|
64
65
|
'orphan' => false,
|
|
65
66
|
'transition' => 'delete'
|
|
66
67
|
}
|
|
67
|
-
|
|
68
|
+
api_call('post', endpoint, headers, body)
|
|
69
|
+
|
|
70
|
+
credentials = @credentials.load_credentials(id, username)
|
|
71
|
+
|
|
72
|
+
wait_for_workspace_deletion(id) do |attempt|
|
|
73
|
+
puts "#{Time.now.inspect} Deleting workspace (attempt #{attempt + 1}/#{5})"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
@credentials.destroy_credentials(credentials, workspace_json(id).dig("latest_build", "status"), id, username)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def wait_for_workspace_deletion(id)
|
|
80
|
+
max_attempts = @credential_deletion_max_attempts
|
|
81
|
+
timeout_interval = @credential_deletion_timeout_interval
|
|
82
|
+
|
|
83
|
+
max_attempts.times do |attempt|
|
|
84
|
+
break unless workspace_json(id) && workspace_json(id).dig("latest_build", "status") == "deleting"
|
|
85
|
+
yield(attempt + 1)
|
|
86
|
+
sleep(timeout_interval)
|
|
87
|
+
end
|
|
68
88
|
end
|
|
69
89
|
|
|
70
|
-
def
|
|
71
|
-
endpoint = "
|
|
90
|
+
def workspace_json(id)
|
|
91
|
+
endpoint = "#{@host}/api/v2/workspaces/#{id}?include_deleted=true"
|
|
72
92
|
headers = get_headers(@token)
|
|
73
|
-
|
|
93
|
+
api_call('get', endpoint, headers)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def info(id)
|
|
97
|
+
workspace_info_from_json(workspace_json(id))
|
|
74
98
|
end
|
|
75
99
|
|
|
76
100
|
def coder_state_to_ood_status(coder_state)
|
|
@@ -137,7 +161,6 @@ class OodCore::Job::Adapters::Coder::Batch
|
|
|
137
161
|
|
|
138
162
|
def api_call(method, endpoint, headers, body = nil)
|
|
139
163
|
uri = URI(endpoint)
|
|
140
|
-
|
|
141
164
|
case method.downcase
|
|
142
165
|
when 'get'
|
|
143
166
|
request = Net::HTTP::Get.new(uri, headers)
|
|
@@ -150,11 +173,9 @@ class OodCore::Job::Adapters::Coder::Batch
|
|
|
150
173
|
end
|
|
151
174
|
|
|
152
175
|
request.body = body.to_json if body
|
|
153
|
-
|
|
154
176
|
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
155
177
|
http.request(request)
|
|
156
178
|
end
|
|
157
|
-
|
|
158
179
|
case response
|
|
159
180
|
when Net::HTTPSuccess
|
|
160
181
|
JSON.parse(response.body)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class CredentialsInterface
|
|
2
|
+
def load_credentials
|
|
3
|
+
raise NotImplementedError, "#{self.class} must implement #{__method__}"
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def generate_credentials
|
|
7
|
+
raise NotImplementedError, "#{self.class} must implement #{__method__}"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def destroy_credentials
|
|
11
|
+
raise NotImplementedError, "#{self.class} must implement #{__method__}"
|
|
12
|
+
end
|
|
13
|
+
def save_credentials
|
|
14
|
+
raise NotImplementedError, "#{self.class} must implement #{__method__}"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
require "fog/openstack"
|
|
2
|
+
require "json"
|
|
3
|
+
require "ood_core/job/adapters/coder/credentials"
|
|
4
|
+
|
|
5
|
+
class OpenStackCredentials < CredentialsInterface
|
|
6
|
+
def initialize(auth_url)
|
|
7
|
+
@auth_url = auth_url
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def load_credentials(id, username)
|
|
11
|
+
file_path = "/home/#{username}/#{id}_credentials.json"
|
|
12
|
+
JSON.parse(File.read(file_path))
|
|
13
|
+
rescue Errno::ENOENT => e
|
|
14
|
+
puts "Error loading credentials: #{e}"
|
|
15
|
+
nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def generate_credentials(project_id, username)
|
|
19
|
+
token_json = JSON.parse(File.read("/home/#{username}/token.json"))
|
|
20
|
+
access_token = token_json["id"]
|
|
21
|
+
user_id = token_json["user_id"]
|
|
22
|
+
connection = Fog::OpenStack::Identity.new({
|
|
23
|
+
openstack_auth_url: @auth_url,
|
|
24
|
+
openstack_management_url: @auth_url,
|
|
25
|
+
openstack_auth_token: access_token,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
auth = {
|
|
29
|
+
"auth": {
|
|
30
|
+
"identity": {
|
|
31
|
+
"methods": [
|
|
32
|
+
"token"
|
|
33
|
+
],
|
|
34
|
+
"token": {
|
|
35
|
+
"id": access_token
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"scope": {
|
|
39
|
+
"project": {
|
|
40
|
+
"id": project_id
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
scoped_token = connection.tokens.authenticate(auth)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
connection = Fog::OpenStack::Identity.new({
|
|
50
|
+
openstack_auth_url: @auth_url,
|
|
51
|
+
openstack_management_url: @auth_url,
|
|
52
|
+
openstack_auth_token: scoped_token,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
app_credentials = {
|
|
57
|
+
"name": "OOD_generated_#{rand(16**8).to_s(16)}" ,
|
|
58
|
+
"description": "Application credential generated via OOD for Coder.",
|
|
59
|
+
"roles": [
|
|
60
|
+
],
|
|
61
|
+
"unrestricted": true,
|
|
62
|
+
"user_id": user_id
|
|
63
|
+
}
|
|
64
|
+
res = connection.application_credentials.create app_credentials
|
|
65
|
+
|
|
66
|
+
credential_data = {
|
|
67
|
+
id: res.id,
|
|
68
|
+
name: res.name,
|
|
69
|
+
user_id: user_id,
|
|
70
|
+
secret: res.secret
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
credential_data
|
|
74
|
+
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def save_credentials(id, username, app_credentials)
|
|
78
|
+
file_path = "/home/#{username}/#{id}_credentials.json"
|
|
79
|
+
File.write(file_path, JSON.generate(app_credentials))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def destroy_credentials(os_app_credentials, deletion_status, id, username)
|
|
84
|
+
return if os_app_credentials.nil?
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
connection = create_fog_connection(os_app_credentials)
|
|
88
|
+
credentials_to_destroy = find_os_application_credentials(connection, os_app_credentials)
|
|
89
|
+
|
|
90
|
+
if deletion_status != "deleted"
|
|
91
|
+
File.delete("/home/#{username}/#{id}_credentials.json")
|
|
92
|
+
puts "Workspace deletion timed out, credentials with id #{os_app_credentials['id']} of user #{os_app_credentials['user_id']} were not destroyed"
|
|
93
|
+
return
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
begin
|
|
97
|
+
credentials_to_destroy.destroy
|
|
98
|
+
rescue Excon::Error::Forbidden => e
|
|
99
|
+
puts "Error destroying application credentials with id #{os_app_credentials['id']} #{e}"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
def create_fog_connection(os_app_credentials)
|
|
106
|
+
Fog::OpenStack::Identity.new({
|
|
107
|
+
openstack_auth_url: @auth_url,
|
|
108
|
+
openstack_management_url: @auth_url,
|
|
109
|
+
openstack_application_credential_id: os_app_credentials['id'],
|
|
110
|
+
openstack_application_credential_secret: os_app_credentials['secret']
|
|
111
|
+
})
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
def find_os_application_credentials(connection, os_app_credentials)
|
|
116
|
+
connection.application_credentials.find_by_id(os_app_credentials['id'], os_app_credentials['user_id'])
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -9,8 +9,16 @@ module OodCore
|
|
|
9
9
|
class Factory
|
|
10
10
|
using Refinements::HashExtensions
|
|
11
11
|
|
|
12
|
+
require "ood_core/job/adapters/coder/openstack_credentials"
|
|
13
|
+
|
|
12
14
|
def self.build_coder(config)
|
|
13
|
-
|
|
15
|
+
config = config.to_h.symbolize_keys
|
|
16
|
+
if config[:auth]["cloud"] == "openstack"
|
|
17
|
+
credentials = OpenStackCredentials.new(config[:auth]["url"])
|
|
18
|
+
else
|
|
19
|
+
raise ArgumentError, "Unsupported credentials for cloud type: #{config[:auth]['cloud']}"
|
|
20
|
+
end
|
|
21
|
+
batch = Adapters::Coder::Batch.new(config.to_h.symbolize_keys, credentials)
|
|
14
22
|
Adapters::Coder.new(batch)
|
|
15
23
|
end
|
|
16
24
|
end
|
|
@@ -18,7 +26,7 @@ module OodCore
|
|
|
18
26
|
module Adapters
|
|
19
27
|
attr_reader :host, :token
|
|
20
28
|
|
|
21
|
-
# The adapter class for
|
|
29
|
+
# The adapter class for Coder.
|
|
22
30
|
class Coder < Adapter
|
|
23
31
|
|
|
24
32
|
using Refinements::ArrayExtensions
|