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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52ba764b085dedb7eaeb06d95751f1804a50488e1859f980a7836d2d9032b95d
4
- data.tar.gz: c2dc5edf395fe158960f33b80c554f3dc745f15e7ec1337b738683a0e1bbdc7f
3
+ metadata.gz: 766b778b98f189dee73ff1cb70a2b0acf53d628a897288260a1b8cf7cb80c0c6
4
+ data.tar.gz: 03052a68c57de5fe76b795dfd76b66639520d3e9b055c324fbece05db71dd331
5
5
  SHA512:
6
- metadata.gz: 59915bae23a008a923c249d222e50548a7bee3438144068a29ae1cafdd489ca1229ee1a14f4f81e3fd065381f46f920bef24344fe633c7c578cb1f6a4f9a2a77
7
- data.tar.gz: 8d2ca42c7f49158c8d321c21b79aff1c636df3c77bb7e71107db70371a34058d79c8a5ec32ca93883e7d3bcc7dc2202375144d23613f167ab089318d6270248c
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
@@ -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.14.0...HEAD
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://travis-ci.org/OSC/ood_core.svg?branch=master)](https://travis-ci.org/OSC/ood_core)
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.each_line do |line|
240
- words = line.split(/\s/).reject(&:empty?)
241
- next if !words.empty? && words[0] == "Id" # just skip the header
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 line_to_hash(words)
250
- return unless words.size == 5
251
-
251
+ def ccqstat_match_to_hash(match)
252
252
  data_hash = {}
253
- data_hash[:id] = words[0]
254
- data_hash[:job_name] = words[1]
255
- data_hash[:job_owner] = words[2]
256
- data_hash[:status] = get_state(words[4])
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, Adapters::Kubernetes::Helper.new)
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
- require "ood_core/job/adapters/kubernetes/helper"
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, :cluster_name, :mounts
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 = {}, helper = Helper.new)
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
- @cluster_name = options.fetch(:cluster_name, 'open-ondemand')
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 = 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.native)
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 = call_json_output('get', 'pod', id)
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
- begin
98
- service_json = call_json_output('get', 'service', service_name(id))
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
- call("#{namespaced_cmd} delete pod #{id}")
115
-
116
- begin
117
- call("#{namespaced_cmd} delete service #{service_name(id)}")
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(native_data)
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
- default_namespace
208
- end
209
-
210
- def default_namespace
211
- username
210
+ "#{namespace_prefix}#{username}"
212
211
  end
213
212
 
214
213
  def context
215
- cluster_name
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
- OodCore::Job::Info.new(hash)
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} #{cluster_name}"
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 #{cluster_name}"
328
- cmd << " --cluster=#{cluster_name} --namespace=#{namespace}"
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 #{cluster_name}"
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, error, s = Open3.capture3(env, cmd, stdin_data: stdin.to_s)
348
- s.success? ? o : raise(Error, error)
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
- require 'ood_core/job/adapters/kubernetes/resources'
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 { native: {host: localhost, port:80, password: sshhh }} in the info
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
- # @return [OodCore::Job::Info]
24
- def info_from_json(pod_json: nil, service_json: nil, secret_json: nil)
25
- pod_hash = pod_info_from_json(pod_json)
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
- # can't just use deep_merge bc we don't depend *directly* on rails
30
- pod_hash[:native] = pod_hash[:native].merge(service_hash[:native])
31
- pod_hash[:native] = pod_hash[:native].merge(secret_hash[:native])
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.dig(:metadata, :namespace).to_s,
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
- native: {
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
- empty_native
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
- empty_native
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
- return nil if status == 'undetermined'
183
+ container_statuses = json_data.dig(:status, :containerStatuses)
184
+ return nil if container_statuses.nil?
196
185
 
197
- state_data = json_data.dig(:status, :containerStatuses)[0].dig(:state)
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
- return nil if status == 'undetermined'
200
+ container_statuses = json_data.dig(:status, :containerStatuses)
201
+ return nil if container_statuses.nil?
212
202
 
213
- state_data = json_data.dig(:status, :containerStatuses)[0].dig(:state)
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
- state = 'undetermined'
254
- status = json_data.dig(:status)
255
- container_statuses = status.dig(:containerStatuses)
256
-
257
- if container_statuses.nil?
258
- # if you're here, it means you're pending, probably unschedulable
259
- return OodCore::Job::Status.new(state: state)
260
- end
261
-
262
- # only support 1 container/pod
263
- json_state = container_statuses[0].dig(:state)
264
- state = 'running' unless json_state.dig(:running).nil?
265
- state = terminated_state(json_state) unless json_state.dig(:terminated).nil?
266
- state = 'queued' unless json_state.dig(:waiting).nil?
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
- end
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
- <% unless spec.container.working_dir.empty? %>
27
+ <%- unless spec.container.working_dir.empty? -%>
21
28
  workingDir: "<%= spec.container.working_dir %>"
22
- <% end %>
23
- <% unless spec.container.env.empty? %>
29
+ <%- end -%>
30
+ <%- unless spec.container.env.empty? -%>
24
31
  env:
25
- <% spec.container.env.each do |env| %>
32
+ <%- spec.container.env.each do |env| -%>
26
33
  - name: <%= env[:name] %>
27
34
  value: "<%= env[:value] %>"
28
- <% end %> <%# for each env %>
29
- <% end %> <%# unless env is nil %>
30
- <% unless spec.container.command.empty? %>
35
+ <%- end # for each env -%>
36
+ <%- end # unless env is nil -%>
37
+ <%- unless spec.container.command.empty? -%>
31
38
  command:
32
- <% spec.container.command.each do |cmd| %>
39
+ <%- spec.container.command.each do |cmd| -%>
33
40
  - "<%= cmd %>"
34
- <% end %> <%# for each command %>
35
- <% end %> <%# unless command is nil %>
36
- <% unless spec.container.port.nil? %>
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
- <% end %>
46
+ <%- end -%>
40
47
  volumeMounts:
41
- <% unless configmap.nil? %>
48
+ <%- unless configmap.nil? -%>
42
49
  - name: configmap-volume
43
50
  mountPath: <%= configmap_mount_path %>
44
- <% end %>
45
- <% all_mounts.each do |mount| %>
51
+ <%- end -%>
52
+ <%- all_mounts.each do |mount| -%>
46
53
  - name: <%= mount[:name] %>
47
54
  mountPath: <%= mount[:destination_path] %>
48
- <% end %> <%# for each mount %>
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
- <% unless spec.init_containers.nil? %>
63
+ <%- unless spec.init_containers.nil? -%>
57
64
  initContainers:
58
- <% spec.init_containers.each do |ctr| %>
65
+ <%- spec.init_containers.each do |ctr| -%>
59
66
  - name: "<%= ctr.name %>"
60
67
  image: "<%= ctr.image %>"
61
68
  command:
62
- <% ctr.command.each do |cmd| %>
69
+ <%- ctr.command.each do |cmd| -%>
63
70
  - "<%= cmd %>"
64
- <% end %> <%# command loop %>
71
+ <%- end # command loop -%>
65
72
  volumeMounts:
66
- <% unless configmap.nil? %>
73
+ <%- unless configmap.nil? -%>
67
74
  - name: configmap-volume
68
75
  mountPath: <%= configmap_mount_path %>
69
- <% end %>
70
- <% all_mounts.each do |mount| %>
76
+ <%- end -%>
77
+ <%- all_mounts.each do |mount| -%>
71
78
  - name: <%= mount[:name] %>
72
79
  mountPath: <%= mount[:destination_path] %>
73
- <% end %> <%# for each mount %>
74
- <% end %> <%# init container loop %>
75
- <% end %> <%# if init containers %>
76
- <% unless configmap.nil? || all_mounts.empty? %>
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
- <% end %> <%# configmap.nil? || all_mounts.empty? %>
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
- <% end %>
84
- <% all_mounts.each do |mount| %>
85
- <% if mount[:type] == 'nfs' %>
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
- <% elsif mount[:type] == 'host' %>
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
- <% end %> <%# if mount is [host,nfs] %>
96
- <% end %> <%# for each mount %>
101
+ <%- end # if mount is [host,nfs] -%>
102
+ <%- end # for each mount -%>
103
+ <%- end # (configmap.to_s.empty? || all_mounts.empty?) -%>
97
104
  ---
98
- <% unless spec.container.port.nil? %>
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
- <% end %> <%# end for service %>
121
+ <%- end # end for service -%>
113
122
  ---
114
- <% unless configmap.nil? %>
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
- <% end %> <%# end for configmap %>
134
+ <%- end # end for configmap -%>
@@ -1,4 +1,4 @@
1
1
  module OodCore
2
2
  # The current version of {OodCore}
3
- VERSION = "0.14.0"
3
+ VERSION = "0.15.0"
4
4
  end
@@ -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.7"
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.14.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: 2020-10-01 00:00:00.000000000 Z
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.7'
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.7'
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.0.8
245
+ rubygems_version: 3.1.2
225
246
  signing_key:
226
247
  specification_version: 4
227
248
  summary: Open OnDemand core library
@@ -1,9 +0,0 @@
1
- sudo: false
2
- language: ruby
3
- rvm:
4
- - 2.5.5
5
- before_install: gem install bundler -v '~> 1.17'
6
- notifications:
7
- email:
8
- on_success: never
9
- on_failure: always