ood_core 0.14.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ OodCore::Job::Adapters::Kubernetes::K8sJobInfo.new(pod_hash)
33
33
  rescue NoMethodError
34
34
  raise K8sDataError, "unable to read data correctly from json"
35
35
  end
@@ -38,18 +38,23 @@ class OodCore::Job::Adapters::Kubernetes::Helper
38
38
  #
39
39
  # @param container [#to_h]
40
40
  # the input container hash
41
+ # @param default_env [#to_h]
42
+ # Default env to merge with defined env
41
43
  # @return [OodCore::Job::Adapters::Kubernetes::Resources::Container]
42
- def container_from_native(container)
43
- Resources::Container.new(
44
+ def container_from_native(container, default_env)
45
+ env = container.fetch(:env, {}).to_h.symbolize_keys
46
+ OodCore::Job::Adapters::Kubernetes::Resources::Container.new(
44
47
  container[:name],
45
48
  container[:image],
46
49
  command: parse_command(container[:command]),
47
50
  port: container[:port],
48
- env: container.fetch(:env, []),
51
+ env: default_env.merge(env),
49
52
  memory: container[:memory],
50
53
  cpu: container[:cpu],
51
54
  working_dir: container[:working_dir],
52
- restart_policy: container[:restart_policy]
55
+ restart_policy: container[:restart_policy],
56
+ image_pull_policy: container[:image_pull_policy],
57
+ image_pull_secret: container[:image_pull_secret]
53
58
  )
54
59
  end
55
60
 
@@ -76,15 +81,22 @@ class OodCore::Job::Adapters::Kubernetes::Helper
76
81
  # the input configmap hash
77
82
  # @param id [#to_s]
78
83
  # the id to use for giving the configmap a name
84
+ # @param script_content [#to_s]
85
+ # the batch script content
79
86
  # @return [OodCore::Job::Adapters::Kubernetes::Resources::ConfigMap]
80
- def configmap_from_native(native, id)
81
- configmap = native.fetch(:configmap, nil)
82
- return nil if configmap.nil?
83
-
84
- Resources::ConfigMap.new(
87
+ def configmap_from_native(native, id, script_content)
88
+ configmap = native.fetch(:configmap, {})
89
+ configmap[:files] ||= []
90
+ configmap[:files] << {
91
+ filename: 'script.sh',
92
+ data: script_content,
93
+ mount_path: '/ood/script.sh',
94
+ sub_path: 'script.sh',
95
+ } unless configmap[:files].any? { |f| f[:filename] == 'script.sh' }
96
+
97
+ OodCore::Job::Adapters::Kubernetes::Resources::ConfigMap.new(
85
98
  configmap_name(id),
86
- configmap[:filename],
87
- configmap[:data]
99
+ (configmap[:files] || [])
88
100
  )
89
101
  end
90
102
 
@@ -93,13 +105,15 @@ class OodCore::Job::Adapters::Kubernetes::Helper
93
105
  # @param native_data [#to_h]
94
106
  # the native data to parse. Expected key init_ctrs and for that
95
107
  # key to be an array of hashes.
108
+ # @param default_env [#to_h]
109
+ # Default env to merge with defined env
96
110
  # @return [Array<OodCore::Job::Adapters::Kubernetes::Resources::Container>]
97
111
  # the array of init containers
98
- def init_ctrs_from_native(ctrs)
112
+ def init_ctrs_from_native(ctrs, default_env)
99
113
  init_ctrs = []
100
114
 
101
115
  ctrs&.each do |ctr_raw|
102
- ctr = container_from_native(ctr_raw)
116
+ ctr = container_from_native(ctr_raw, default_env)
103
117
  init_ctrs.push(ctr)
104
118
  end
105
119
 
@@ -118,25 +132,29 @@ class OodCore::Job::Adapters::Kubernetes::Helper
118
132
  id + '-configmap'
119
133
  end
120
134
 
135
+ def seconds_to_duration(s)
136
+ "%02dh%02dm%02ds" % [s / 3600, s / 60 % 60, s % 60]
137
+ end
138
+
121
139
  # Extract pod info from json data. The data is expected to be from the kubectl
122
140
  # command and conform to kubernetes' datatype structures.
123
141
  #
124
142
  # @param json_data [#to_h]
125
143
  # the pod data returned from 'kubectl get pod abc-123'
144
+ # @param ns_prefix [#to_s]
145
+ # the namespace prefix so that namespaces can be converted back to usernames
126
146
  # @return [#to_h]
127
147
  # the hash of info expected from adapters
128
- def pod_info_from_json(json_data)
148
+ def pod_info_from_json(json_data, ns_prefix: nil)
129
149
  {
130
150
  id: json_data.dig(:metadata, :name).to_s,
131
151
  job_name: name_from_metadata(json_data.dig(:metadata)),
132
152
  status: pod_status_from_json(json_data),
133
- job_owner: json_data.dig(:metadata, :namespace).to_s,
153
+ job_owner: job_owner_from_json(json_data, ns_prefix),
134
154
  submission_time: submission_time(json_data),
135
155
  dispatch_time: dispatch_time(json_data),
136
156
  wallclock_time: wallclock_time(json_data),
137
- native: {
138
- host: get_host(json_data.dig(:status, :hostIP))
139
- },
157
+ ood_connection_info: { host: get_host(json_data.dig(:status, :hostIP)) },
140
158
  procs: procs_from_json(json_data)
141
159
  }
142
160
  rescue NoMethodError
@@ -162,39 +180,24 @@ class OodCore::Job::Adapters::Kubernetes::Helper
162
180
  def service_info_from_json(json_data)
163
181
  # all we need is the port - .spec.ports[0].nodePort
164
182
  ports = json_data.dig(:spec, :ports)
165
- {
166
- native:
167
- {
168
- port: ports[0].dig(:nodePort)
169
- }
170
- }
183
+ { ood_connection_info: { port: ports[0].dig(:nodePort) } }
171
184
  rescue
172
- empty_native
185
+ {}
173
186
  end
174
187
 
175
188
  def secret_info_from_json(json_data)
176
189
  raw = json_data.dig(:data, :password)
177
- {
178
- native:
179
- {
180
- password: Base64.decode64(raw)
181
- }
182
- }
190
+ { ood_connection_info: { password: Base64.decode64(raw) } }
183
191
  rescue
184
- empty_native
185
- end
186
-
187
- def empty_native
188
- {
189
- native: {}
190
- }
192
+ {}
191
193
  end
192
194
 
193
195
  def dispatch_time(json_data)
194
196
  status = pod_status_from_json(json_data)
195
- return nil if status == 'undetermined'
197
+ container_statuses = json_data.dig(:status, :containerStatuses)
198
+ return nil if container_statuses.nil?
196
199
 
197
- state_data = json_data.dig(:status, :containerStatuses)[0].dig(:state)
200
+ state_data = container_statuses[0].dig(:state)
198
201
  date_string = nil
199
202
 
200
203
  if status == 'completed'
@@ -208,9 +211,10 @@ class OodCore::Job::Adapters::Kubernetes::Helper
208
211
 
209
212
  def wallclock_time(json_data)
210
213
  status = pod_status_from_json(json_data)
211
- return nil if status == 'undetermined'
214
+ container_statuses = json_data.dig(:status, :containerStatuses)
215
+ return nil if container_statuses.nil?
212
216
 
213
- state_data = json_data.dig(:status, :containerStatuses)[0].dig(:state)
217
+ state_data = container_statuses[0].dig(:state)
214
218
  start_time = dispatch_time(json_data)
215
219
  return nil if start_time.nil?
216
220
 
@@ -250,20 +254,21 @@ class OodCore::Job::Adapters::Kubernetes::Helper
250
254
  end
251
255
 
252
256
  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?
257
+ phase = json_data.dig(:status, :phase)
258
+ state = case phase
259
+ when "Running"
260
+ "running"
261
+ when "Pending"
262
+ "queued"
263
+ when "Failed"
264
+ "suspended"
265
+ when "Succeeded"
266
+ "completed"
267
+ when "Unknown"
268
+ "undetermined"
269
+ else
270
+ "undetermined"
271
+ end
267
272
 
268
273
  OodCore::Job::Status.new(state: state)
269
274
  end
@@ -295,4 +300,9 @@ class OodCore::Job::Adapters::Kubernetes::Helper
295
300
  cpu
296
301
  end
297
302
  end
298
- end
303
+
304
+ def job_owner_from_json(json_data = {}, ns_prefix = nil)
305
+ namespace = json_data.dig(:metadata, :namespace).to_s
306
+ namespace.delete_prefix(ns_prefix.to_s)
307
+ end
308
+ 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
@@ -1,22 +1,45 @@
1
1
  module OodCore::Job::Adapters::Kubernetes::Resources
2
2
 
3
3
  class ConfigMap
4
- attr_accessor :name, :filename, :data
4
+ attr_accessor :name, :files
5
5
 
6
- def initialize(name, filename, data)
6
+ def initialize(name, files)
7
7
  @name = name
8
- @filename = filename
9
- @data = data
8
+ @files = []
9
+ files.each do |f|
10
+ @files << ConfigMapFile.new(f)
11
+ end
12
+ end
13
+
14
+ def mounts?
15
+ @files.any? { |f| f.mount_path }
16
+ end
17
+
18
+ def init_mounts?
19
+ @files.any? { |f| f.init_mount_path }
20
+ end
21
+ end
22
+
23
+ class ConfigMapFile
24
+ attr_accessor :filename, :data, :mount_path, :sub_path, :init_mount_path, :init_sub_path
25
+
26
+ def initialize(data)
27
+ @filename = data[:filename]
28
+ @data = data[:data]
29
+ @mount_path = data[:mount_path]
30
+ @sub_path = data[:sub_path]
31
+ @init_mount_path = data[:init_mount_path]
32
+ @init_sub_path = data[:init_sub_path]
10
33
  end
11
34
  end
12
35
 
13
36
  class Container
14
37
  attr_accessor :name, :image, :command, :port, :env, :memory, :cpu, :working_dir,
15
- :restart_policy
38
+ :restart_policy, :image_pull_policy, :image_pull_secret, :supplemental_groups
16
39
 
17
40
  def initialize(
18
- name, image, command: [], port: nil, env: [], memory: "4Gi", cpu: "1",
19
- working_dir: "", restart_policy: "Never"
41
+ name, image, command: [], port: nil, env: {}, memory: "4Gi", cpu: "1",
42
+ working_dir: "", restart_policy: "Never", image_pull_policy: nil, image_pull_secret: nil, supplemental_groups: []
20
43
  )
21
44
  raise ArgumentError, "containers need valid names and images" unless name && image
22
45
 
@@ -24,11 +47,14 @@ module OodCore::Job::Adapters::Kubernetes::Resources
24
47
  @image = image
25
48
  @command = command.nil? ? [] : command
26
49
  @port = port&.to_i
27
- @env = env.nil? ? [] : env
50
+ @env = env.nil? ? {} : env
28
51
  @memory = memory.nil? ? "4Gi" : memory
29
52
  @cpu = cpu.nil? ? "1" : cpu
30
53
  @working_dir = working_dir.nil? ? "" : working_dir
31
54
  @restart_policy = restart_policy.nil? ? "Never" : restart_policy
55
+ @image_pull_policy = image_pull_policy.nil? ? "IfNotPresent" : image_pull_policy
56
+ @image_pull_secret = image_pull_secret
57
+ @supplemental_groups = supplemental_groups.nil? ? [] : supplemental_groups
32
58
  end
33
59
 
34
60
  def ==(other)
@@ -40,9 +66,11 @@ module OodCore::Job::Adapters::Kubernetes::Resources
40
66
  memory == other.memory &&
41
67
  cpu == other.cpu &&
42
68
  working_dir == other.working_dir &&
43
- restart_policy == other.restart_policy
69
+ restart_policy == other.restart_policy &&
70
+ image_pull_policy == other.image_pull_policy &&
71
+ image_pull_secret == other.image_pull_secret &&
72
+ supplemental_groups == other.supplemental_groups
44
73
  end
45
-
46
74
  end
47
75
 
48
76
  class PodSpec
@@ -7,100 +7,187 @@ 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:
13
20
  runAsUser: <%= run_as_user %>
14
21
  runAsGroup: <%= run_as_group %>
22
+ runAsNonRoot: true
23
+ <%- if spec.container.supplemental_groups.empty? -%>
24
+ supplementalGroups: []
25
+ <%- else -%>
26
+ supplementalGroups:
27
+ <%- spec.container.supplemental_groups.each do |supplemental_group| -%>
28
+ - "<%= supplemental_group %>"
29
+ <%- end -%>
30
+ <%- end -%>
15
31
  fsGroup: <%= fs_group %>
32
+ hostNetwork: false
33
+ hostIPC: false
34
+ hostPID: false
35
+ <%- unless spec.container.image_pull_secret.nil? -%>
36
+ imagePullSecrets:
37
+ - name: <%= spec.container.image_pull_secret %>
38
+ <%- end -%>
16
39
  containers:
17
40
  - name: "<%= spec.container.name %>"
18
41
  image: <%= spec.container.image %>
19
- imagePullPolicy: IfNotPresent
20
- <% unless spec.container.working_dir.empty? %>
42
+ imagePullPolicy: <%= spec.container.image_pull_policy %>
43
+ <%- unless spec.container.working_dir.empty? -%>
21
44
  workingDir: "<%= spec.container.working_dir %>"
22
- <% end %>
23
- <% unless spec.container.env.empty? %>
45
+ <%- end -%>
24
46
  env:
25
- <% spec.container.env.each do |env| %>
26
- - name: <%= env[:name] %>
27
- value: "<%= env[:value] %>"
28
- <% end %> <%# for each env %>
29
- <% end %> <%# unless env is nil %>
30
- <% unless spec.container.command.empty? %>
47
+ - name: POD_NAME
48
+ valueFrom:
49
+ fieldRef:
50
+ fieldPath: metadata.name
51
+ - name: POD_NAMESPACE
52
+ valueFrom:
53
+ fieldRef:
54
+ fieldPath: metadata.namespace
55
+ <%- spec.container.env.each_pair do |name, value| -%>
56
+ - name: <%= name %>
57
+ value: "<%= value %>"
58
+ <%- end # for each env -%>
59
+ <%- unless spec.container.command.empty? -%>
31
60
  command:
32
- <% spec.container.command.each do |cmd| %>
61
+ <%- spec.container.command.each do |cmd| -%>
33
62
  - "<%= cmd %>"
34
- <% end %> <%# for each command %>
35
- <% end %> <%# unless command is nil %>
36
- <% unless spec.container.port.nil? %>
63
+ <%- end # for each command -%>
64
+ <%- end # unless command is nil -%>
65
+ <%- unless spec.container.port.nil? -%>
37
66
  ports:
38
67
  - containerPort: <%= spec.container.port %>
39
- <% end %>
68
+ <%- end -%>
69
+ <%- if !all_mounts.empty? || (!configmap.nil? && configmap.mounts?) -%>
40
70
  volumeMounts:
41
- <% unless configmap.nil? %>
71
+ <%- unless configmap.nil? -%>
72
+ <%- configmap.files.each do |file| -%>
73
+ <%- next if file.mount_path.nil? -%>
42
74
  - name: configmap-volume
43
- mountPath: <%= configmap_mount_path %>
44
- <% end %>
45
- <% all_mounts.each do |mount| %>
75
+ mountPath: <%= file.mount_path %>
76
+ <%- unless file.sub_path.nil? -%>
77
+ subPath: <%= file.sub_path %>
78
+ <%- end # end unless file.sub_path.nil? -%>
79
+ <%- end # end configmap.files.each -%>
80
+ <%- end # unless configmap.nil? -%>
81
+ <%- all_mounts.each do |mount| -%>
46
82
  - name: <%= mount[:name] %>
47
83
  mountPath: <%= mount[:destination_path] %>
48
- <% end %> <%# for each mount %>
84
+ <%- end # for each mount -%>
85
+ <%- end # configmap mounts? and all_mounts not empty -%>
49
86
  resources:
50
87
  limits:
51
88
  memory: "<%= spec.container.memory %>"
52
89
  cpu: "<%= spec.container.cpu %>"
90
+ <%- unless script.gpus_per_node.nil? -%>
91
+ <%= gpu_type %>: <%= script.gpus_per_node %>
92
+ <%- end -%>
53
93
  requests:
54
94
  memory: "<%= spec.container.memory %>"
55
95
  cpu: "<%= spec.container.cpu %>"
56
- <% unless spec.init_containers.nil? %>
96
+ <%- unless script.gpus_per_node.nil? -%>
97
+ <%= gpu_type %>: <%= script.gpus_per_node %>
98
+ <%- end -%>
99
+ securityContext:
100
+ allowPrivilegeEscalation: false
101
+ capabilities:
102
+ drop:
103
+ - all
104
+ privileged: false
105
+ <%- unless spec.init_containers.empty? -%>
57
106
  initContainers:
58
- <% spec.init_containers.each do |ctr| %>
107
+ <%- spec.init_containers.each do |ctr| -%>
59
108
  - name: "<%= ctr.name %>"
60
109
  image: "<%= ctr.image %>"
110
+ imagePullPolicy: <%= ctr.image_pull_policy %>
111
+ env:
112
+ - name: POD_NAME
113
+ valueFrom:
114
+ fieldRef:
115
+ fieldPath: metadata.name
116
+ - name: POD_NAMESPACE
117
+ valueFrom:
118
+ fieldRef:
119
+ fieldPath: metadata.namespace
120
+ <%- ctr.env.each_pair do |name, value| -%>
121
+ - name: <%= name %>
122
+ value: "<%= value %>"
123
+ <%- end # for each env -%>
61
124
  command:
62
- <% ctr.command.each do |cmd| %>
125
+ <%- ctr.command.each do |cmd| -%>
63
126
  - "<%= cmd %>"
64
- <% end %> <%# command loop %>
127
+ <%- end # command loop -%>
128
+ <%- if !all_mounts.empty? || (!configmap.nil? && configmap.init_mounts?) -%>
65
129
  volumeMounts:
66
- <% unless configmap.nil? %>
130
+ <%- unless configmap.nil? -%>
131
+ <%- configmap.files.each do |file| -%>
132
+ <%- next if file.init_mount_path.nil? -%>
67
133
  - name: configmap-volume
68
- mountPath: <%= configmap_mount_path %>
69
- <% end %>
70
- <% all_mounts.each do |mount| %>
134
+ mountPath: <%= file.init_mount_path %>
135
+ <%- unless file.init_sub_path.nil? -%>
136
+ subPath: <%= file.init_sub_path %>
137
+ <%- end # end unless file.sub_path.nil? -%>
138
+ <%- end # end configmap.files.each -%>
139
+ <%- end # unless configmap.nil? -%>
140
+ <%- all_mounts.each do |mount| -%>
71
141
  - name: <%= mount[:name] %>
72
142
  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? %>
143
+ <%- end # for each mount -%>
144
+ <%- end # if config_map init mounts and all_mounts not empty -%>
145
+ securityContext:
146
+ allowPrivilegeEscalation: false
147
+ capabilities:
148
+ drop:
149
+ - all
150
+ privileged: false
151
+ <%- end # init container loop -%>
152
+ <%- end # if init containers -%>
153
+ <%- unless (configmap.to_s.empty? && all_mounts.empty?) -%>
77
154
  volumes:
78
- <% end %> <%# configmap.nil? || all_mounts.empty? %>
79
- <% unless configmap.nil? %>
155
+ <%- unless configmap.to_s.empty? -%>
80
156
  - name: configmap-volume
81
157
  configMap:
82
158
  name: <%= configmap_name(id) %>
83
- <% end %>
84
- <% all_mounts.each do |mount| %>
85
- <% if mount[:type] == 'nfs' %>
159
+ <%- end -%>
160
+ <%- all_mounts.each do |mount| -%>
161
+ <%- if mount[:type] == 'nfs' -%>
86
162
  - name: <%= mount[:name] %>
87
163
  nfs:
88
164
  server: <%= mount[:host] %>
89
165
  path: <%= mount[:path] %>
90
- <% elsif mount[:type] == 'host' %>
166
+ <%- elsif mount[:type] == 'host' -%>
91
167
  - name: <%= mount[:name] %>
92
168
  hostPath:
93
169
  path: <%= mount[:path] %>
94
170
  type: <%= mount[:host_type] %>
95
- <% end %> <%# if mount is [host,nfs] %>
96
- <% end %> <%# for each mount %>
171
+ <%- end # if mount is [host,nfs] -%>
172
+ <%- end # for each mount -%>
173
+ <%- end # (configmap.to_s.empty? || all_mounts.empty?) -%>
174
+ <%- unless node_selector.empty? -%>
175
+ nodeSelector:
176
+ <%- node_selector.each_pair do |key, value| -%>
177
+ <%= key %>: "<%= value %>"
178
+ <%- end # node_selector.each_pair -%>
179
+ <%- end #unless node_selector.empty? -%>
97
180
  ---
98
- <% unless spec.container.port.nil? %>
181
+ <%- unless spec.container.port.nil? -%>
99
182
  apiVersion: v1
100
183
  kind: Service
101
184
  metadata:
102
185
  name: <%= service_name(id) %>
103
186
  namespace: <%= namespace %>
187
+ labels:
188
+ job: <%= id %>
189
+ app.kubernetes.io/name: <%= container.name %>
190
+ app.kubernetes.io/managed-by: open-ondemand
104
191
  spec:
105
192
  selector:
106
193
  job: <%= id %>
@@ -109,15 +196,22 @@ spec:
109
196
  port: 80
110
197
  targetPort: <%= spec.container.port %>
111
198
  type: NodePort
112
- <% end %> <%# end for service %>
199
+ <%- end # end for service -%>
200
+ <%- unless configmap.nil? -%>
113
201
  ---
114
- <% unless configmap.nil? %>
115
202
  apiVersion: v1
116
203
  kind: ConfigMap
117
204
  metadata:
118
205
  name: <%= configmap_name(id) %>
119
206
  namespace: <%= namespace %>
207
+ labels:
208
+ job: <%= id %>
209
+ app.kubernetes.io/name: <%= container.name %>
210
+ app.kubernetes.io/managed-by: open-ondemand
120
211
  data:
121
- <%= configmap.filename %>: |
122
- <% config_data_lines(configmap.data).each do |line| %><%= line %><% end %>
123
- <% end %> <%# end for configmap %>
212
+ <%- configmap.files.each do |file| -%>
213
+ <%- next if file.data.nil? || file.filename.nil? -%>
214
+ <%= file.filename %>: |
215
+ <% config_data_lines(file.data).each do |line| %><%= line %><% end %>
216
+ <%- end # end for configmap files -%>
217
+ <%- end # end configmap.nil? %>