ood_core 0.14.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +30 -0
- data/CHANGELOG.md +20 -1
- data/README.md +1 -1
- data/lib/ood_core/job/adapters/ccq.rb +19 -12
- data/lib/ood_core/job/adapters/kubernetes.rb +1 -1
- data/lib/ood_core/job/adapters/kubernetes/batch.rb +49 -45
- data/lib/ood_core/job/adapters/kubernetes/helper.rb +54 -58
- data/lib/ood_core/job/adapters/kubernetes/k8s_job_info.rb +9 -0
- data/lib/ood_core/job/adapters/kubernetes/templates/pod.yml.erb +50 -39
- data/lib/ood_core/version.rb +1 -1
- data/ood_core.gemspec +2 -1
- metadata +27 -6
- data/.travis.yml +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 766b778b98f189dee73ff1cb70a2b0acf53d628a897288260a1b8cf7cb80c0c6
|
4
|
+
data.tar.gz: 03052a68c57de5fe76b795dfd76b66639520d3e9b055c324fbece05db71dd331
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c678069d0a37762a706a020c5a7ad7a7354ed3f1edb01fdd25b915065b50754d43c60df48c9ea3b773f3dc4ddb4e12604e9dda02a1ad30b0482e7ab050804181
|
7
|
+
data.tar.gz: 8416227140b6d761f6246f0cce50e41f4462f83b1913ad9e72b511685412810b7b2c7c9951f8c712b127572b421e8a57cd6cdb4dedb7cab9c242eba0d057ad45
|
@@ -0,0 +1,30 @@
|
|
1
|
+
name: Unit Tests
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- master
|
7
|
+
pull_request:
|
8
|
+
branches:
|
9
|
+
- master
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
tests:
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
|
15
|
+
steps:
|
16
|
+
- name: checkout
|
17
|
+
uses: actions/checkout@v2
|
18
|
+
|
19
|
+
- name: Setup Ruby using Bundler
|
20
|
+
uses: ruby/setup-ruby@v1
|
21
|
+
with:
|
22
|
+
ruby-version: "2.7.1"
|
23
|
+
bundler-cache: true
|
24
|
+
bundler: "2.1.4"
|
25
|
+
|
26
|
+
- name: install gems
|
27
|
+
run: bundle install
|
28
|
+
|
29
|
+
- name: test
|
30
|
+
run: bundle exec rake spec
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,24 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
|
+
## [0.15.0] - 2021-01-26
|
10
|
+
### Fixed
|
11
|
+
- ccq adapter now accepts job names with spaces in [210](https://github.com/OSC/ood_core/pull/209)
|
12
|
+
- k8s correctly handles having no mount volumes in [239](https://github.com/OSC/ood_core/pull/239)
|
13
|
+
|
14
|
+
### Added
|
15
|
+
- k8s adapter now applies account metadata to resources in [216](https://github.com/OSC/ood_core/pull/216) and
|
16
|
+
[231](https://github.com/OSC/ood_core/pull/231)
|
17
|
+
- k8s adapter can now prefix namespaces in [218](https://github.com/OSC/ood_core/pull/218)
|
18
|
+
- k8s adapter now applies time limits to pods in [224](https://github.com/OSC/ood_core/pull/224)
|
19
|
+
|
20
|
+
### Changed
|
21
|
+
- testing automation is now done in github actions in [221](https://github.com/OSC/ood_core/pull/218)
|
22
|
+
- update bunlder to 2.1.4 and ruby to 2.7 in [235](https://github.com/OSC/ood_core/pull/218) updated bundler and ruby
|
23
|
+
- k8s adapter more appropriately labels unschedulable pods as queued in [230](https://github.com/OSC/ood_core/pull/230)
|
24
|
+
- k8s adapter now uses the script#ood_connection_info API instead of script#native in
|
25
|
+
[222](https://github.com/OSC/ood_core/pull/222)
|
26
|
+
|
9
27
|
## [0.14.0] - 2020-10-01
|
10
28
|
### Added
|
11
29
|
- Kubernetes adapter in PR [156](https://github.com/OSC/ood_core/pull/156)
|
@@ -255,7 +273,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|
255
273
|
### Added
|
256
274
|
- Initial release!
|
257
275
|
|
258
|
-
[Unreleased]: https://github.com/OSC/ood_core/compare/v0.
|
276
|
+
[Unreleased]: https://github.com/OSC/ood_core/compare/v0.15.0...HEAD
|
277
|
+
[0.15.0]: https://github.com/OSC/ood_core/compare/v0.14.0...v0.15.0
|
259
278
|
[0.14.0]: https://github.com/OSC/ood_core/compare/v0.13.0...v0.14.0
|
260
279
|
[0.13.0]: https://github.com/OSC/ood_core/compare/v0.12.0...v0.13.0
|
261
280
|
[0.12.0]: https://github.com/OSC/ood_core/compare/v0.11.4...v0.12.0
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# OodCore
|
2
2
|
|
3
|
-
[![Build Status](https://
|
3
|
+
[![Build Status](https://github.com/osc/ood_core/workflows/Unit%20Tests/badge.svg)](https://github.com/OSC/ood_core/actions?query=workflow%3A%22Unit+Tests%22)
|
4
4
|
![GitHub Release](https://img.shields.io/github/release/osc/ood_core.svg)
|
5
5
|
![GitHub License](https://img.shields.io/github/license/osc/ood_core.svg)
|
6
6
|
|
@@ -203,6 +203,10 @@ module OodCore
|
|
203
203
|
'ccq_ood_script_'
|
204
204
|
end
|
205
205
|
|
206
|
+
def ccqstat_regex
|
207
|
+
/^(?<id>\S+)\s+(?<name>.+)\s+(?<username>\S+)\s+(?<scheduler>\S+)\s+(?<status>\S+)\s*$/
|
208
|
+
end
|
209
|
+
|
206
210
|
def parse_job_id_from_ccqsub(output)
|
207
211
|
match_data = /#{jobid_regex}/.match(output)
|
208
212
|
# match_data could be nil, OR re-configured jobid_regex could be looking for a different named group
|
@@ -236,28 +240,31 @@ module OodCore
|
|
236
240
|
def info_from_ccqstat(data)
|
237
241
|
infos = []
|
238
242
|
|
239
|
-
data.to_s.
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
infos << Info.new(line_to_hash(words)) if words.size == 5
|
243
|
+
data.to_s.lines.drop(1).each do |line|
|
244
|
+
match_data = ccqstat_regex.match(line)
|
245
|
+
infos << Info.new(ccqstat_match_to_hash(match_data)) if valid_ccqstat_match?(match_data)
|
244
246
|
end
|
245
247
|
|
246
248
|
infos
|
247
249
|
end
|
248
250
|
|
249
|
-
def
|
250
|
-
return unless words.size == 5
|
251
|
-
|
251
|
+
def ccqstat_match_to_hash(match)
|
252
252
|
data_hash = {}
|
253
|
-
data_hash[:id] =
|
254
|
-
data_hash[:
|
255
|
-
data_hash[:
|
256
|
-
|
253
|
+
data_hash[:id] = match.named_captures.fetch('id', nil)
|
254
|
+
data_hash[:job_owner] = match.named_captures.fetch('username', nil)
|
255
|
+
data_hash[:status] = get_state(match.named_captures.fetch('status', nil))
|
256
|
+
|
257
|
+
# The regex leaves trailing empty spaces. There's no way to tell if they're _actually_
|
258
|
+
# a part of the job name or not, so we assume they're not and add the rstrip.
|
259
|
+
data_hash[:job_name] = match.named_captures.fetch('name', nil).to_s.rstrip
|
257
260
|
|
258
261
|
data_hash
|
259
262
|
end
|
260
263
|
|
264
|
+
def valid_ccqstat_match?(match)
|
265
|
+
!match.nil? && !match.named_captures.fetch('id', nil).nil?
|
266
|
+
end
|
267
|
+
|
261
268
|
def get_state(state)
|
262
269
|
STATE_MAP.fetch(state, :undetermined)
|
263
270
|
end
|
@@ -7,7 +7,7 @@ module OodCore
|
|
7
7
|
using Refinements::HashExtensions
|
8
8
|
|
9
9
|
def self.build_kubernetes(config)
|
10
|
-
batch = Adapters::Kubernetes::Batch.new(config.to_h.symbolize_keys
|
10
|
+
batch = Adapters::Kubernetes::Batch.new(config.to_h.symbolize_keys)
|
11
11
|
Adapters::Kubernetes.new(batch)
|
12
12
|
end
|
13
13
|
end
|
@@ -3,31 +3,31 @@ require "json"
|
|
3
3
|
|
4
4
|
class OodCore::Job::Adapters::Kubernetes::Batch
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
Helper = OodCore::Job::Adapters::Kubernetes::Helper
|
9
|
-
Resources = OodCore::Job::Adapters::Kubernetes::Resources
|
6
|
+
require_relative "helper"
|
7
|
+
require_relative "k8s_job_info"
|
10
8
|
|
11
9
|
using OodCore::Refinements::HashExtensions
|
12
10
|
|
13
11
|
class Error < StandardError; end
|
12
|
+
class NotFoundError < StandardError; end
|
14
13
|
|
15
|
-
attr_reader :config_file, :bin, :
|
14
|
+
attr_reader :config_file, :bin, :cluster, :mounts
|
16
15
|
attr_reader :all_namespaces, :using_context, :helper
|
17
|
-
attr_reader :username_prefix
|
16
|
+
attr_reader :username_prefix, :namespace_prefix
|
18
17
|
|
19
|
-
def initialize(options = {}
|
18
|
+
def initialize(options = {})
|
20
19
|
options = options.to_h.symbolize_keys
|
21
20
|
|
22
21
|
@config_file = options.fetch(:config_file, default_config_file)
|
23
22
|
@bin = options.fetch(:bin, '/usr/bin/kubectl')
|
24
|
-
@
|
23
|
+
@cluster = options.fetch(:cluster, 'open-ondemand')
|
25
24
|
@mounts = options.fetch(:mounts, []).map { |m| m.to_h.symbolize_keys }
|
26
25
|
@all_namespaces = options.fetch(:all_namespaces, false)
|
27
26
|
@username_prefix = options.fetch(:username_prefix, nil)
|
27
|
+
@namespace_prefix = options.fetch(:namespace_prefix, '')
|
28
28
|
|
29
29
|
@using_context = false
|
30
|
-
@helper =
|
30
|
+
@helper = Helper.new
|
31
31
|
|
32
32
|
begin
|
33
33
|
make_kubectl_config(options)
|
@@ -44,7 +44,7 @@ class OodCore::Job::Adapters::Kubernetes::Batch
|
|
44
44
|
def submit(script, after: [], afterok: [], afternotok: [], afterany: [])
|
45
45
|
raise ArgumentError, 'Must specify the script' if script.nil?
|
46
46
|
|
47
|
-
resource_yml, id = generate_id_yml(script
|
47
|
+
resource_yml, id = generate_id_yml(script)
|
48
48
|
call("#{formatted_ns_cmd} create -f -", stdin: resource_yml)
|
49
49
|
|
50
50
|
id
|
@@ -92,16 +92,11 @@ class OodCore::Job::Adapters::Kubernetes::Batch
|
|
92
92
|
end
|
93
93
|
|
94
94
|
def info(id)
|
95
|
-
pod_json =
|
95
|
+
pod_json = safe_call('get', 'pod', id)
|
96
|
+
return OodCore::Job::Info.new({ id: id, status: 'completed' }) if pod_json.empty?
|
96
97
|
|
97
|
-
|
98
|
-
|
99
|
-
secret_json = call_json_output('get', 'secret', secret_name(id))
|
100
|
-
rescue
|
101
|
-
# it's ok if these don't exist
|
102
|
-
service_json ||= nil
|
103
|
-
secret_json ||= nil
|
104
|
-
end
|
98
|
+
service_json = safe_call('get', 'service', service_name(id))
|
99
|
+
secret_json = safe_call('get', 'secret', secret_name(id))
|
105
100
|
|
106
101
|
helper.info_from_json(pod_json: pod_json, service_json: service_json, secret_json: secret_json)
|
107
102
|
end
|
@@ -111,16 +106,10 @@ class OodCore::Job::Adapters::Kubernetes::Batch
|
|
111
106
|
end
|
112
107
|
|
113
108
|
def delete(id)
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
call("#{namespaced_cmd} delete secret #{secret_name(id)}")
|
119
|
-
call("#{namespaced_cmd} delete configmap #{configmap_name(id)}")
|
120
|
-
rescue
|
121
|
-
# FIXME: retries? delete if exists?
|
122
|
-
# just eat the results of deleting services and secrets
|
123
|
-
end
|
109
|
+
safe_call("delete", "pod", id)
|
110
|
+
safe_call("delete", "service", service_name(id))
|
111
|
+
safe_call("delete", "secret", secret_name(id))
|
112
|
+
safe_call("delete", "configmap", configmap_name(id))
|
124
113
|
end
|
125
114
|
|
126
115
|
def configmap_mount_path
|
@@ -129,6 +118,19 @@ class OodCore::Job::Adapters::Kubernetes::Batch
|
|
129
118
|
|
130
119
|
private
|
131
120
|
|
121
|
+
def safe_call(verb, resource, id)
|
122
|
+
begin
|
123
|
+
case verb.to_s
|
124
|
+
when "get"
|
125
|
+
call_json_output('get', resource, id)
|
126
|
+
when "delete"
|
127
|
+
call("#{namespaced_cmd} delete #{resource} #{id}")
|
128
|
+
end
|
129
|
+
rescue NotFoundError
|
130
|
+
{}
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
132
134
|
# helper to help format multi-line yaml data from the submit.yml into
|
133
135
|
# mutli-line yaml in the pod.yml.erb
|
134
136
|
def config_data_lines(data)
|
@@ -165,15 +167,16 @@ class OodCore::Job::Adapters::Kubernetes::Batch
|
|
165
167
|
|
166
168
|
# helper to template resource yml you're going to submit and
|
167
169
|
# create an id.
|
168
|
-
def generate_id_yml(
|
170
|
+
def generate_id_yml(script)
|
171
|
+
native_data = script.native
|
169
172
|
container = helper.container_from_native(native_data[:container])
|
170
173
|
id = generate_id(container.name)
|
171
174
|
configmap = helper.configmap_from_native(native_data, id)
|
172
175
|
init_containers = helper.init_ctrs_from_native(native_data[:init_containers])
|
173
|
-
spec = Resources::PodSpec.new(container, init_containers: init_containers)
|
176
|
+
spec = Kubernetes::Resources::PodSpec.new(container, init_containers: init_containers)
|
174
177
|
all_mounts = native_data[:mounts].nil? ? mounts : mounts + native_data[:mounts]
|
175
178
|
|
176
|
-
template = ERB.new(File.read(resource_file))
|
179
|
+
template = ERB.new(File.read(resource_file), nil, '-')
|
177
180
|
|
178
181
|
[template.result(binding), id]
|
179
182
|
end
|
@@ -204,15 +207,11 @@ class OodCore::Job::Adapters::Kubernetes::Batch
|
|
204
207
|
end
|
205
208
|
|
206
209
|
def namespace
|
207
|
-
|
208
|
-
end
|
209
|
-
|
210
|
-
def default_namespace
|
211
|
-
username
|
210
|
+
"#{namespace_prefix}#{username}"
|
212
211
|
end
|
213
212
|
|
214
213
|
def context
|
215
|
-
|
214
|
+
cluster
|
216
215
|
end
|
217
216
|
|
218
217
|
def default_config_file
|
@@ -264,7 +263,7 @@ class OodCore::Job::Adapters::Kubernetes::Batch
|
|
264
263
|
|
265
264
|
def pod_info_from_json(pod)
|
266
265
|
hash = helper.pod_info_from_json(pod)
|
267
|
-
|
266
|
+
K8sJobInfo.new(hash)
|
268
267
|
rescue Helper::K8sDataError
|
269
268
|
# FIXME: silently eating error, could probably use a logger
|
270
269
|
nil
|
@@ -318,14 +317,14 @@ class OodCore::Job::Adapters::Kubernetes::Batch
|
|
318
317
|
locale = "--region=#{region}" unless region.nil?
|
319
318
|
|
320
319
|
# gke cluster name can probably can differ from what ood calls the cluster
|
321
|
-
cmd = "gcloud container clusters get-credentials #{locale} #{
|
320
|
+
cmd = "gcloud container clusters get-credentials #{locale} #{cluster}"
|
322
321
|
env = { 'KUBECONFIG' => config_file }
|
323
322
|
call(cmd, env)
|
324
323
|
end
|
325
324
|
|
326
325
|
def set_context
|
327
|
-
cmd = "#{base_cmd} config set-context #{
|
328
|
-
cmd << " --cluster=#{
|
326
|
+
cmd = "#{base_cmd} config set-context #{cluster}"
|
327
|
+
cmd << " --cluster=#{cluster} --namespace=#{namespace}"
|
329
328
|
cmd << " --user=#{k8s_username}"
|
330
329
|
|
331
330
|
call(cmd)
|
@@ -336,7 +335,7 @@ class OodCore::Job::Adapters::Kubernetes::Batch
|
|
336
335
|
server = config.fetch(:endpoint)
|
337
336
|
cert = config.fetch(:cert_authority_file, nil)
|
338
337
|
|
339
|
-
cmd = "#{base_cmd} config set-cluster #{
|
338
|
+
cmd = "#{base_cmd} config set-cluster #{cluster}"
|
340
339
|
cmd << " --server=#{server}"
|
341
340
|
cmd << " --certificate-authority=#{cert}" unless cert.nil?
|
342
341
|
|
@@ -344,7 +343,12 @@ class OodCore::Job::Adapters::Kubernetes::Batch
|
|
344
343
|
end
|
345
344
|
|
346
345
|
def call(cmd = '', env: {}, stdin: nil)
|
347
|
-
o,
|
348
|
-
s.success? ? o :
|
346
|
+
o, e, s = Open3.capture3(env, cmd, stdin_data: stdin.to_s)
|
347
|
+
s.success? ? o : interpret_and_raise(e)
|
348
|
+
end
|
349
|
+
|
350
|
+
def interpret_and_raise(stderr)
|
351
|
+
raise NotFoundError, stderr if /^Error from server \(NotFound\):/.match(stderr)
|
352
|
+
raise(Error, stderr)
|
349
353
|
end
|
350
354
|
end
|
@@ -1,18 +1,17 @@
|
|
1
1
|
class OodCore::Job::Adapters::Kubernetes::Helper
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'resources'
|
4
|
+
require_relative 'k8s_job_info'
|
4
5
|
require 'resolv'
|
5
6
|
require 'base64'
|
7
|
+
require 'active_support/core_ext/hash'
|
6
8
|
|
7
9
|
class K8sDataError < StandardError; end
|
8
10
|
|
9
|
-
Resources = OodCore::Job::Adapters::Kubernetes::Resources
|
10
|
-
|
11
11
|
# Extract info from json data. The data is expected to be from the kubectl
|
12
12
|
# command and conform to kubernetes' datatype structures.
|
13
13
|
#
|
14
|
-
# Returns
|
15
|
-
# object field in lieu of writing a connection.yml
|
14
|
+
# Returns K8sJobInfo in the in lieu of writing a connection.yml
|
16
15
|
#
|
17
16
|
# @param pod_json [#to_h]
|
18
17
|
# the pod data returned from 'kubectl get pod abc-123'
|
@@ -20,16 +19,17 @@ class OodCore::Job::Adapters::Kubernetes::Helper
|
|
20
19
|
# the service data returned from 'kubectl get service abc-123-service'
|
21
20
|
# @param secret_json [#to_h]
|
22
21
|
# the secret data returned from 'kubectl get secret abc-123-secret'
|
23
|
-
# @
|
24
|
-
|
25
|
-
|
22
|
+
# @param ns_prefix [#to_s]
|
23
|
+
# the namespace prefix so that namespaces can be converted back to usernames
|
24
|
+
# @return [OodCore::Job::Adapters::Kubernetes::K8sJobInfo]
|
25
|
+
def info_from_json(pod_json: nil, service_json: nil, secret_json: nil, ns_prefix: nil)
|
26
|
+
pod_hash = pod_info_from_json(pod_json, ns_prefix: ns_prefix)
|
26
27
|
service_hash = service_info_from_json(service_json)
|
27
28
|
secret_hash = secret_info_from_json(secret_json)
|
28
29
|
|
29
|
-
|
30
|
-
pod_hash
|
31
|
-
|
32
|
-
OodCore::Job::Info.new(pod_hash)
|
30
|
+
pod_hash.deep_merge!(service_hash)
|
31
|
+
pod_hash.deep_merge!(secret_hash)
|
32
|
+
K8sJobInfo.new(pod_hash)
|
33
33
|
rescue NoMethodError
|
34
34
|
raise K8sDataError, "unable to read data correctly from json"
|
35
35
|
end
|
@@ -40,7 +40,7 @@ class OodCore::Job::Adapters::Kubernetes::Helper
|
|
40
40
|
# the input container hash
|
41
41
|
# @return [OodCore::Job::Adapters::Kubernetes::Resources::Container]
|
42
42
|
def container_from_native(container)
|
43
|
-
Resources::Container.new(
|
43
|
+
Kubernetes::Resources::Container.new(
|
44
44
|
container[:name],
|
45
45
|
container[:image],
|
46
46
|
command: parse_command(container[:command]),
|
@@ -81,7 +81,7 @@ class OodCore::Job::Adapters::Kubernetes::Helper
|
|
81
81
|
configmap = native.fetch(:configmap, nil)
|
82
82
|
return nil if configmap.nil?
|
83
83
|
|
84
|
-
Resources::ConfigMap.new(
|
84
|
+
Kubernetes::Resources::ConfigMap.new(
|
85
85
|
configmap_name(id),
|
86
86
|
configmap[:filename],
|
87
87
|
configmap[:data]
|
@@ -118,25 +118,29 @@ class OodCore::Job::Adapters::Kubernetes::Helper
|
|
118
118
|
id + '-configmap'
|
119
119
|
end
|
120
120
|
|
121
|
+
def seconds_to_duration(s)
|
122
|
+
"%02dh%02dm%02ds" % [s / 3600, s / 60 % 60, s % 60]
|
123
|
+
end
|
124
|
+
|
121
125
|
# Extract pod info from json data. The data is expected to be from the kubectl
|
122
126
|
# command and conform to kubernetes' datatype structures.
|
123
127
|
#
|
124
128
|
# @param json_data [#to_h]
|
125
129
|
# the pod data returned from 'kubectl get pod abc-123'
|
130
|
+
# @param ns_prefix [#to_s]
|
131
|
+
# the namespace prefix so that namespaces can be converted back to usernames
|
126
132
|
# @return [#to_h]
|
127
133
|
# the hash of info expected from adapters
|
128
|
-
def pod_info_from_json(json_data)
|
134
|
+
def pod_info_from_json(json_data, ns_prefix: nil)
|
129
135
|
{
|
130
136
|
id: json_data.dig(:metadata, :name).to_s,
|
131
137
|
job_name: name_from_metadata(json_data.dig(:metadata)),
|
132
138
|
status: pod_status_from_json(json_data),
|
133
|
-
job_owner: json_data
|
139
|
+
job_owner: job_owner_from_json(json_data, ns_prefix),
|
134
140
|
submission_time: submission_time(json_data),
|
135
141
|
dispatch_time: dispatch_time(json_data),
|
136
142
|
wallclock_time: wallclock_time(json_data),
|
137
|
-
|
138
|
-
host: get_host(json_data.dig(:status, :hostIP))
|
139
|
-
},
|
143
|
+
ood_connection_info: { host: get_host(json_data.dig(:status, :hostIP)) },
|
140
144
|
procs: procs_from_json(json_data)
|
141
145
|
}
|
142
146
|
rescue NoMethodError
|
@@ -162,39 +166,24 @@ class OodCore::Job::Adapters::Kubernetes::Helper
|
|
162
166
|
def service_info_from_json(json_data)
|
163
167
|
# all we need is the port - .spec.ports[0].nodePort
|
164
168
|
ports = json_data.dig(:spec, :ports)
|
165
|
-
{
|
166
|
-
native:
|
167
|
-
{
|
168
|
-
port: ports[0].dig(:nodePort)
|
169
|
-
}
|
170
|
-
}
|
169
|
+
{ ood_connection_info: { port: ports[0].dig(:nodePort) } }
|
171
170
|
rescue
|
172
|
-
|
171
|
+
{}
|
173
172
|
end
|
174
173
|
|
175
174
|
def secret_info_from_json(json_data)
|
176
175
|
raw = json_data.dig(:data, :password)
|
177
|
-
{
|
178
|
-
native:
|
179
|
-
{
|
180
|
-
password: Base64.decode64(raw)
|
181
|
-
}
|
182
|
-
}
|
176
|
+
{ ood_connection_info: { password: Base64.decode64(raw) } }
|
183
177
|
rescue
|
184
|
-
|
185
|
-
end
|
186
|
-
|
187
|
-
def empty_native
|
188
|
-
{
|
189
|
-
native: {}
|
190
|
-
}
|
178
|
+
{}
|
191
179
|
end
|
192
180
|
|
193
181
|
def dispatch_time(json_data)
|
194
182
|
status = pod_status_from_json(json_data)
|
195
|
-
|
183
|
+
container_statuses = json_data.dig(:status, :containerStatuses)
|
184
|
+
return nil if container_statuses.nil?
|
196
185
|
|
197
|
-
state_data =
|
186
|
+
state_data = container_statuses[0].dig(:state)
|
198
187
|
date_string = nil
|
199
188
|
|
200
189
|
if status == 'completed'
|
@@ -208,9 +197,10 @@ class OodCore::Job::Adapters::Kubernetes::Helper
|
|
208
197
|
|
209
198
|
def wallclock_time(json_data)
|
210
199
|
status = pod_status_from_json(json_data)
|
211
|
-
|
200
|
+
container_statuses = json_data.dig(:status, :containerStatuses)
|
201
|
+
return nil if container_statuses.nil?
|
212
202
|
|
213
|
-
state_data =
|
203
|
+
state_data = container_statuses[0].dig(:state)
|
214
204
|
start_time = dispatch_time(json_data)
|
215
205
|
return nil if start_time.nil?
|
216
206
|
|
@@ -250,20 +240,21 @@ class OodCore::Job::Adapters::Kubernetes::Helper
|
|
250
240
|
end
|
251
241
|
|
252
242
|
def pod_status_from_json(json_data)
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
243
|
+
phase = json_data.dig(:status, :phase)
|
244
|
+
state = case phase
|
245
|
+
when "Running"
|
246
|
+
"running"
|
247
|
+
when "Pending"
|
248
|
+
"queued"
|
249
|
+
when "Failed"
|
250
|
+
"suspended"
|
251
|
+
when "Succeeded"
|
252
|
+
"completed"
|
253
|
+
when "Unknown"
|
254
|
+
"undetermined"
|
255
|
+
else
|
256
|
+
"undetermined"
|
257
|
+
end
|
267
258
|
|
268
259
|
OodCore::Job::Status.new(state: state)
|
269
260
|
end
|
@@ -295,4 +286,9 @@ class OodCore::Job::Adapters::Kubernetes::Helper
|
|
295
286
|
cpu
|
296
287
|
end
|
297
288
|
end
|
298
|
-
|
289
|
+
|
290
|
+
def job_owner_from_json(json_data = {}, ns_prefix = nil)
|
291
|
+
namespace = json_data.dig(:metadata, :namespace).to_s
|
292
|
+
namespace.delete_prefix(ns_prefix.to_s)
|
293
|
+
end
|
294
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# An object that describes a submitted kubernetes job with extended information
|
2
|
+
class OodCore::Job::Adapters::Kubernetes::K8sJobInfo < OodCore::Job::Info
|
3
|
+
attr_reader :ood_connection_info
|
4
|
+
|
5
|
+
def initialize(ood_connection_info: {}, **options)
|
6
|
+
super(options)
|
7
|
+
@ood_connection_info = ood_connection_info
|
8
|
+
end
|
9
|
+
end
|
@@ -7,6 +7,13 @@ metadata:
|
|
7
7
|
job: <%= id %>
|
8
8
|
app.kubernetes.io/name: <%= container.name %>
|
9
9
|
app.kubernetes.io/managed-by: open-ondemand
|
10
|
+
<%- if !script.accounting_id.nil? && script.accounting_id != "" -%>
|
11
|
+
account: <%= script.accounting_id %>
|
12
|
+
<%- end -%>
|
13
|
+
annotations:
|
14
|
+
<%- unless script.wall_time.nil? -%>
|
15
|
+
pod.kubernetes.io/lifetime: <%= helper.seconds_to_duration(script.wall_time) %>
|
16
|
+
<%- end -%>
|
10
17
|
spec:
|
11
18
|
restartPolicy: <%= spec.container.restart_policy %>
|
12
19
|
securityContext:
|
@@ -17,35 +24,35 @@ spec:
|
|
17
24
|
- name: "<%= spec.container.name %>"
|
18
25
|
image: <%= spec.container.image %>
|
19
26
|
imagePullPolicy: IfNotPresent
|
20
|
-
|
27
|
+
<%- unless spec.container.working_dir.empty? -%>
|
21
28
|
workingDir: "<%= spec.container.working_dir %>"
|
22
|
-
|
23
|
-
|
29
|
+
<%- end -%>
|
30
|
+
<%- unless spec.container.env.empty? -%>
|
24
31
|
env:
|
25
|
-
|
32
|
+
<%- spec.container.env.each do |env| -%>
|
26
33
|
- name: <%= env[:name] %>
|
27
34
|
value: "<%= env[:value] %>"
|
28
|
-
|
29
|
-
|
30
|
-
|
35
|
+
<%- end # for each env -%>
|
36
|
+
<%- end # unless env is nil -%>
|
37
|
+
<%- unless spec.container.command.empty? -%>
|
31
38
|
command:
|
32
|
-
|
39
|
+
<%- spec.container.command.each do |cmd| -%>
|
33
40
|
- "<%= cmd %>"
|
34
|
-
|
35
|
-
|
36
|
-
|
41
|
+
<%- end # for each command -%>
|
42
|
+
<%- end # unless command is nil -%>
|
43
|
+
<%- unless spec.container.port.nil? -%>
|
37
44
|
ports:
|
38
45
|
- containerPort: <%= spec.container.port %>
|
39
|
-
|
46
|
+
<%- end -%>
|
40
47
|
volumeMounts:
|
41
|
-
|
48
|
+
<%- unless configmap.nil? -%>
|
42
49
|
- name: configmap-volume
|
43
50
|
mountPath: <%= configmap_mount_path %>
|
44
|
-
|
45
|
-
|
51
|
+
<%- end -%>
|
52
|
+
<%- all_mounts.each do |mount| -%>
|
46
53
|
- name: <%= mount[:name] %>
|
47
54
|
mountPath: <%= mount[:destination_path] %>
|
48
|
-
|
55
|
+
<%- end # for each mount -%>
|
49
56
|
resources:
|
50
57
|
limits:
|
51
58
|
memory: "<%= spec.container.memory %>"
|
@@ -53,54 +60,56 @@ spec:
|
|
53
60
|
requests:
|
54
61
|
memory: "<%= spec.container.memory %>"
|
55
62
|
cpu: "<%= spec.container.cpu %>"
|
56
|
-
|
63
|
+
<%- unless spec.init_containers.nil? -%>
|
57
64
|
initContainers:
|
58
|
-
|
65
|
+
<%- spec.init_containers.each do |ctr| -%>
|
59
66
|
- name: "<%= ctr.name %>"
|
60
67
|
image: "<%= ctr.image %>"
|
61
68
|
command:
|
62
|
-
|
69
|
+
<%- ctr.command.each do |cmd| -%>
|
63
70
|
- "<%= cmd %>"
|
64
|
-
|
71
|
+
<%- end # command loop -%>
|
65
72
|
volumeMounts:
|
66
|
-
|
73
|
+
<%- unless configmap.nil? -%>
|
67
74
|
- name: configmap-volume
|
68
75
|
mountPath: <%= configmap_mount_path %>
|
69
|
-
|
70
|
-
|
76
|
+
<%- end -%>
|
77
|
+
<%- all_mounts.each do |mount| -%>
|
71
78
|
- name: <%= mount[:name] %>
|
72
79
|
mountPath: <%= mount[:destination_path] %>
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
80
|
+
<%- end # for each mount -%>
|
81
|
+
<%- end # init container loop -%>
|
82
|
+
<%- end # if init containers -%>
|
83
|
+
<%- unless (configmap.to_s.empty? && all_mounts.empty?) -%>
|
77
84
|
volumes:
|
78
|
-
|
79
|
-
<% unless configmap.nil? %>
|
85
|
+
<%- unless configmap.to_s.empty? -%>
|
80
86
|
- name: configmap-volume
|
81
87
|
configMap:
|
82
88
|
name: <%= configmap_name(id) %>
|
83
|
-
|
84
|
-
|
85
|
-
|
89
|
+
<%- end -%>
|
90
|
+
<%- all_mounts.each do |mount| -%>
|
91
|
+
<%- if mount[:type] == 'nfs' -%>
|
86
92
|
- name: <%= mount[:name] %>
|
87
93
|
nfs:
|
88
94
|
server: <%= mount[:host] %>
|
89
95
|
path: <%= mount[:path] %>
|
90
|
-
|
96
|
+
<%- elsif mount[:type] == 'host' -%>
|
91
97
|
- name: <%= mount[:name] %>
|
92
98
|
hostPath:
|
93
99
|
path: <%= mount[:path] %>
|
94
100
|
type: <%= mount[:host_type] %>
|
95
|
-
|
96
|
-
|
101
|
+
<%- end # if mount is [host,nfs] -%>
|
102
|
+
<%- end # for each mount -%>
|
103
|
+
<%- end # (configmap.to_s.empty? || all_mounts.empty?) -%>
|
97
104
|
---
|
98
|
-
|
105
|
+
<%- unless spec.container.port.nil? -%>
|
99
106
|
apiVersion: v1
|
100
107
|
kind: Service
|
101
108
|
metadata:
|
102
109
|
name: <%= service_name(id) %>
|
103
110
|
namespace: <%= namespace %>
|
111
|
+
labels:
|
112
|
+
job: <%= id %>
|
104
113
|
spec:
|
105
114
|
selector:
|
106
115
|
job: <%= id %>
|
@@ -109,15 +118,17 @@ spec:
|
|
109
118
|
port: 80
|
110
119
|
targetPort: <%= spec.container.port %>
|
111
120
|
type: NodePort
|
112
|
-
|
121
|
+
<%- end # end for service -%>
|
113
122
|
---
|
114
|
-
|
123
|
+
<%- unless configmap.nil? -%>
|
115
124
|
apiVersion: v1
|
116
125
|
kind: ConfigMap
|
117
126
|
metadata:
|
118
127
|
name: <%= configmap_name(id) %>
|
119
128
|
namespace: <%= namespace %>
|
129
|
+
labels:
|
130
|
+
job: <%= id %>
|
120
131
|
data:
|
121
132
|
<%= configmap.filename %>: |
|
122
133
|
<% config_data_lines(configmap.data).each do |line| %><%= line %><% end %>
|
123
|
-
|
134
|
+
<%- end # end for configmap -%>
|
data/lib/ood_core/version.rb
CHANGED
data/ood_core.gemspec
CHANGED
@@ -24,7 +24,8 @@ 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.9", ">= 1.9.6"
|
27
|
-
spec.add_development_dependency "bundler", "~> 1
|
27
|
+
spec.add_development_dependency "bundler", "~> 2.1"
|
28
|
+
spec.add_runtime_dependency "activesupport", ">= 5.2", "< 6.0"
|
28
29
|
spec.add_development_dependency "rake", "~> 13.0.1"
|
29
30
|
spec.add_development_dependency "rspec", "~> 3.0"
|
30
31
|
spec.add_development_dependency "pry", "~> 0.10"
|
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.
|
4
|
+
version: 0.15.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:
|
13
|
+
date: 2021-01-26 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: ood_support
|
@@ -52,14 +52,34 @@ dependencies:
|
|
52
52
|
requirements:
|
53
53
|
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: '1
|
55
|
+
version: '2.1'
|
56
56
|
type: :development
|
57
57
|
prerelease: false
|
58
58
|
version_requirements: !ruby/object:Gem::Requirement
|
59
59
|
requirements:
|
60
60
|
- - "~>"
|
61
61
|
- !ruby/object:Gem::Version
|
62
|
-
version: '1
|
62
|
+
version: '2.1'
|
63
|
+
- !ruby/object:Gem::Dependency
|
64
|
+
name: activesupport
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '5.2'
|
70
|
+
- - "<"
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '6.0'
|
73
|
+
type: :runtime
|
74
|
+
prerelease: false
|
75
|
+
version_requirements: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '5.2'
|
80
|
+
- - "<"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '6.0'
|
63
83
|
- !ruby/object:Gem::Dependency
|
64
84
|
name: rake
|
65
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -140,9 +160,9 @@ executables: []
|
|
140
160
|
extensions: []
|
141
161
|
extra_rdoc_files: []
|
142
162
|
files:
|
163
|
+
- ".github/workflows/test.yml"
|
143
164
|
- ".gitignore"
|
144
165
|
- ".rspec"
|
145
|
-
- ".travis.yml"
|
146
166
|
- CHANGELOG.md
|
147
167
|
- Gemfile
|
148
168
|
- LICENSE.txt
|
@@ -169,6 +189,7 @@ files:
|
|
169
189
|
- lib/ood_core/job/adapters/kubernetes.rb
|
170
190
|
- lib/ood_core/job/adapters/kubernetes/batch.rb
|
171
191
|
- lib/ood_core/job/adapters/kubernetes/helper.rb
|
192
|
+
- lib/ood_core/job/adapters/kubernetes/k8s_job_info.rb
|
172
193
|
- lib/ood_core/job/adapters/kubernetes/resources.rb
|
173
194
|
- lib/ood_core/job/adapters/kubernetes/templates/pod.yml.erb
|
174
195
|
- lib/ood_core/job/adapters/linux_host.rb
|
@@ -221,7 +242,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
221
242
|
- !ruby/object:Gem::Version
|
222
243
|
version: '0'
|
223
244
|
requirements: []
|
224
|
-
rubygems_version: 3.
|
245
|
+
rubygems_version: 3.1.2
|
225
246
|
signing_key:
|
226
247
|
specification_version: 4
|
227
248
|
summary: Open OnDemand core library
|