ood_core 0.29.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 53043c13d393367627c85321c8c7ef9d69d7a6cbab687ea32a82ab2077484024
4
- data.tar.gz: e6c1f60a01e714e5ac02090a9c01ce7b6e96bde96d1568b31c60ab630cecc3c5
3
+ metadata.gz: 14dedae6a9b9508f87bb9b950520fb4614f345a90d8841c23836c2650a5b5d2f
4
+ data.tar.gz: 357f6a0eec8b1029f6ad1d32983a55e071b6fda90b453953a8400c5e7f442c68
5
5
  SHA512:
6
- metadata.gz: a5ee3699f5737abf3158e00341fd7e236b6320893f9a3640f7e5b61c854796512e03277c699db100ebeef402f735a58e82596b50dce152a381b01f43cb93ce3b
7
- data.tar.gz: 5ff0fc3ed64f3154394aeedc5caed25fd0779304963dbbecf0b6b00083185459abfa40a3787da43f0da21eb475c30bb726c26514e7395ece23b193081e4f2cb1
6
+ metadata.gz: 0c7782f857836971b0f26207669c9ef21746b0490755a0e86ba0eea6d4b75069c61e7a5c349aea813b4c38f812e099725d97ba0b7d8e916a3cd00e859e12e4eb
7
+ data.tar.gz: 798c9a6709dbba8d704e51942e9677ca632df7c773689dafeaebbe2212f560c2a18643f15dfa8550bdc1bff810eda34b929ce9fd4d84fef10001a68d63533056
@@ -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
@@ -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 for the current user.
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 get_os_app_credentials(username, project_id)
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: os_app_credentials["name"] },
22
- { name: "application_credential_id", value: os_app_credentials["id"] },
23
- { name: "application_credential_secret", value: os_app_credentials["secret"] },
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 = "https://#{@host}/api/v2/organizations/#{org_id}/members/#{username}/workspaces"
47
- os_app_credentials = get_os_app_credentials(username, project_id)
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
- template_id: script.native[:template_id],
51
- template_version_name: script.native[:template_version_name],
52
- name: "#{username}-#{script.native[:workspace_name]}-#{rand(2_821_109_907_456).to_s(36)}",
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 = "https://#{@host}/api/v2/workspaces/#{id}/builds"
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
- res = api_call('post', endpoint, headers, body)
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 info(id)
71
- endpoint = "https://#{@host}/api/v2/workspaces/#{id}?include_deleted=true"
90
+ def workspace_json(id)
91
+ endpoint = "#{@host}/api/v2/workspaces/#{id}?include_deleted=true"
72
92
  headers = get_headers(@token)
73
- workspace_info_from_json(api_call('get', endpoint, headers))
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
- batch = Adapters::Coder::Batch.new(config.to_h.symbolize_keys)
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 Kubernetes.
29
+ # The adapter class for Coder.
22
30
  class Coder < Adapter
23
31
 
24
32
  using Refinements::ArrayExtensions
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
  SSH_HOSTS=(<%= ssh_hosts.join(' ').to_s %>)
3
- hostnames=`hostname -A`
3
+ hostnames=`hostname -A 2>/dev/null || hostname`
4
4
  for host in ${SSH_HOSTS[@]}
5
5
  do
6
6
  if [[ " ${hostnames[@]} " =~ " ${host} " ]]; then
@@ -62,16 +62,19 @@ class OodCore::Job::Adapters::Sge::Batch
62
62
  # @param owner [#to_s] the owner or owner list
63
63
  # @return [Array<OodCore::Job::Info>]
64
64
  def get_all(owner: nil)
65
- listener = QstatXmlRListener.new
66
- argv = ['qstat', '-r', '-xml']
67
- argv.concat ['-u', owner] unless owner.nil?
68
- REXML::Parsers::StreamParser.new(call(*argv), listener).parse
69
-
70
- listener.parsed_jobs.map{
71
- |job_hash| OodCore::Job::Info.new(
72
- **post_process_qstat_job_hash(job_hash)
73
- )
74
- }
65
+ begin
66
+ listener = QstatXmlRListener.new
67
+ argv = ['qstat', '-r', '-xml']
68
+ argv.concat ['-u', owner] unless owner.nil?
69
+ REXML::Parsers::StreamParser.new(call(*argv), listener).parse
70
+
71
+ listener.parsed_jobs.map do |job_hash|
72
+ OodCore::Job::Info.new(**post_process_qstat_job_hash(job_hash))
73
+ end
74
+ rescue REXML::ParseException => e
75
+ warn("Error parsing response: #{e}")
76
+ []
77
+ end
75
78
  end
76
79
 
77
80
  # Get OodCore::Job::Info for a job_id that may still be in the queue
@@ -25,7 +25,13 @@ module OodCore
25
25
  bin_overrides = c.fetch(:bin_overrides, {})
26
26
  submit_host = c.fetch(:submit_host, "")
27
27
  strict_host_checking = c.fetch(:strict_host_checking, true)
28
- slurm = Adapters::Slurm::Batch.new(cluster: cluster, conf: conf, bin: bin, bin_overrides: bin_overrides, submit_host: submit_host, strict_host_checking: strict_host_checking)
28
+ id = c.fetch(:id, 'unknown')
29
+
30
+ slurm = Adapters::Slurm::Batch.new(
31
+ cluster: cluster, conf: conf, bin: bin, bin_overrides: bin_overrides,
32
+ submit_host: submit_host, strict_host_checking: strict_host_checking,
33
+ id: id
34
+ )
29
35
  Adapters::Slurm.new(slurm: slurm)
30
36
  end
31
37
  end
@@ -84,6 +90,11 @@ module OodCore
84
90
  # @return [Bool]; true if empty
85
91
  attr_reader :strict_host_checking
86
92
 
93
+ # The ID of the cluster.
94
+ # @example oakley
95
+ # @return [String]; The ID of the cluster.
96
+ attr_reader :id
97
+
87
98
  # The root exception class that all Slurm-specific exceptions inherit
88
99
  # from
89
100
  class Error < StandardError; end
@@ -97,13 +108,14 @@ module OodCore
97
108
  # @param bin_overrides [#to_h] a hash of bin ovverides to be used in job
98
109
  # @param submit_host [#to_s] Submits the job on a login node via ssh
99
110
  # @param strict_host_checking [Bool] Whether to use strict host checking when ssh to submit_host
100
- def initialize(cluster: nil, bin: nil, conf: nil, bin_overrides: {}, submit_host: "", strict_host_checking: true)
111
+ def initialize(cluster: nil, bin: nil, conf: nil, bin_overrides: {}, submit_host: "", strict_host_checking: true, id: 'unknown')
101
112
  @cluster = cluster && cluster.to_s
102
113
  @conf = conf && Pathname.new(conf.to_s)
103
114
  @bin = Pathname.new(bin.to_s)
104
115
  @bin_overrides = bin_overrides
105
116
  @submit_host = submit_host.to_s
106
117
  @strict_host_checking = strict_host_checking
118
+ @id = id.to_s
107
119
  end
108
120
 
109
121
  # Get a ClusterInfo object containing information about the given cluster
@@ -182,22 +194,29 @@ module OodCore
182
194
 
183
195
  def accounts
184
196
  user = Etc.getlogin
185
- args = ['-nP', 'show', 'users', 'withassoc', 'format=account,cluster,partition,qos', 'where', "user=#{user}"]
197
+ args = [
198
+ '-nP', 'show', 'users', 'withassoc', 'format=account,qos',
199
+ 'where', "user=#{user}", "cluster=#{id}"
200
+ ]
186
201
 
187
- [].tap do |accts|
202
+ [].tap do |associations|
188
203
  call('sacctmgr', *args).each_line do |line|
189
- acct, cluster, queue, qos = line.split('|')
204
+ acct, qos = line.split('|')
190
205
  next if acct.nil? || acct.chomp.empty?
191
206
 
192
- args = {
207
+ associations << {
193
208
  name: acct,
194
209
  qos: qos.to_s.chomp.split(','),
195
- cluster: cluster,
196
- queue: queue.to_s.empty? ? nil : queue
197
210
  }
198
- info = OodCore::Job::AccountInfo.new(**args) unless acct.nil?
199
- accts << info unless acct.nil?
200
211
  end
212
+ end.group_by do |x|
213
+ [x[:name], x[:cluster]]
214
+ end.map do |(name, cluster), assocs|
215
+ OodCore::Job::AccountInfo.new(
216
+ name: name,
217
+ cluster: cluster,
218
+ qos: (assocs.flat_map { |x| x[:qos] }).uniq,
219
+ )
201
220
  end
202
221
  end
203
222
 
@@ -430,7 +449,6 @@ module OodCore
430
449
  end.to_h.symbolize_keys
431
450
 
432
451
  hsh[:name] = hsh[:PartitionName]
433
- hsh[:qos] = hsh[:QoS].to_s == 'N/A' ? [] : hsh[:QoS].to_s.split(',')
434
452
  hsh[:allow_accounts] = if hsh[:AllowAccounts].nil? || hsh[:AllowAccounts].to_s == 'ALL'
435
453
  nil
436
454
  else
@@ -443,6 +461,16 @@ module OodCore
443
461
  hsh[:DenyAccounts].nil? ? [] : hsh[:DenyAccounts].to_s.split(',')
444
462
  end
445
463
 
464
+ hsh[:allow_qos] = if hsh[:AllowQos].nil? || hsh[:AllowQos].to_s == 'ALL'
465
+ []
466
+ else
467
+ hsh[:AllowQos].to_s.split(',')
468
+ end
469
+ hsh[:deny_qos] = if !hsh[:allow_qos].empty?
470
+ [] # manpage says that AllowQos negates DenyQos
471
+ else
472
+ hsh[:DenyQos].nil? ? [] : hsh[:DenyQos].to_s.split(',')
473
+ end
446
474
  hsh[:tres] = case hsh[:TRES]
447
475
  when nil, '(null)', ''
448
476
  {}
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
  SSH_HOSTS=(<%= ssh_hosts.join(' ').to_s %>)
3
- hostnames=`hostname -A`
3
+ hostnames=`hostname -A 2>/dev/null || hostname`
4
4
  for host in ${SSH_HOSTS[@]}
5
5
  do
6
6
  if [[ " ${hostnames[@]} " =~ " ${host} " ]]; then
@@ -10,7 +10,8 @@ class OodCore::Job::QueueInfo
10
10
  alias to_s name
11
11
 
12
12
  # The QoSes associated with this queue
13
- attr_reader :qos
13
+ attr_reader :allow_qos
14
+ attr_reader :deny_qos
14
15
 
15
16
  # The accounts that are allowed to use this queue.
16
17
  #
@@ -25,7 +26,8 @@ class OodCore::Job::QueueInfo
25
26
 
26
27
  def initialize(**opts)
27
28
  @name = opts.fetch(:name, 'unknown')
28
- @qos = opts.fetch(:qos, [])
29
+ @allow_qos = opts.fetch(:allow_qos, [])
30
+ @deny_qos = opts.fetch(:deny_qos, [])
29
31
  @tres = opts.fetch(:tres, {})
30
32
 
31
33
  allow_accounts = opts.fetch(:allow_accounts, nil)
@@ -50,4 +52,8 @@ class OodCore::Job::QueueInfo
50
52
  def gpu?
51
53
  tres.keys.any? { |name| name.to_s.match?(%r{^gres/gpu($|:)}i) }
52
54
  end
55
+
56
+ def allow_all_qos?
57
+ allow_qos.empty? && deny_qos.empty?
58
+ end
53
59
  end
@@ -1,4 +1,4 @@
1
1
  module OodCore
2
2
  # The current version of {OodCore}
3
- VERSION = "0.29.0"
3
+ VERSION = "0.30.0"
4
4
  end
data/ood_core.gemspec CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.add_runtime_dependency "ood_support", "~> 0.0.2"
26
26
  spec.add_runtime_dependency "ffi", "~> 1.16.3"
27
+ spec.add_runtime_dependency "fog-openstack", "~> 1.1.5"
27
28
  spec.add_runtime_dependency "rexml", "~> 3.2"
28
29
  spec.add_development_dependency "bundler", "~> 2.1"
29
30
  spec.add_development_dependency "rake", "~> 13.3.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ood_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.29.0
4
+ version: 0.30.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Franz
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2025-08-08 00:00:00.000000000 Z
13
+ date: 2025-11-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: ood_support
@@ -40,6 +40,20 @@ dependencies:
40
40
  - - "~>"
41
41
  - !ruby/object:Gem::Version
42
42
  version: 1.16.3
43
+ - !ruby/object:Gem::Dependency
44
+ name: fog-openstack
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: 1.1.5
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: 1.1.5
43
57
  - !ruby/object:Gem::Dependency
44
58
  name: rexml
45
59
  requirement: !ruby/object:Gem::Requirement
@@ -207,6 +221,8 @@ files:
207
221
  - lib/ood_core/job/adapters/coder.rb
208
222
  - lib/ood_core/job/adapters/coder/batch.rb
209
223
  - lib/ood_core/job/adapters/coder/coder_job_info.rb
224
+ - lib/ood_core/job/adapters/coder/credentials.rb
225
+ - lib/ood_core/job/adapters/coder/openstack_credentials.rb
210
226
  - lib/ood_core/job/adapters/drmaa.rb
211
227
  - lib/ood_core/job/adapters/fujitsu_tcs.rb
212
228
  - lib/ood_core/job/adapters/helper.rb