@@ -142,6 +142,55 @@ def create(spec: Dict[str, Any], name: str, namespace: str, **_: Any) -> Dict[st
142
142
logging .info ("Starting create (name=%s namespace=%s)..." , name , namespace )
143
143
logging .info ("spec=%s (name=%s)" , spec , name )
144
144
145
+ # All Data-Manager provided material
146
+ # will be namespaced under the 'imDataManager' property
147
+ material : Dict [str , Any ] = spec .get ("imDataManager" , {})
148
+
149
+ notebook_interface = material .get ("notebook" , {}).get ("interface" , "lab" )
150
+
151
+ image = material .get ("image" , _DEFAULT_IMAGE )
152
+ image_parts = image .split (":" )
153
+ image_tag = "latest" if len (image_parts ) == 1 else image_parts [1 ]
154
+ image_pull_policy = (
155
+ "Always" if image_tag .lower () in ["latest" , "stable" ] else "IfNotPresent"
156
+ )
157
+
158
+ service_account = material .get ("serviceAccountName" , _DEFAULT_SA )
159
+
160
+ resources = material .get ("resources" , {})
161
+ cpu_limit = resources .get ("limits" , {}).get ("cpu" , _DEFAULT_CPU_LIMIT )
162
+ cpu_request = resources .get ("requests" , {}).get ("cpu" , _DEFAULT_CPU_REQUEST )
163
+ memory_limit = resources .get ("limits" , {}).get ("memory" , _DEFAULT_MEM_LIMIT )
164
+ memory_request = resources .get ("requests" , {}).get ("memory" , _DEFAULT_MEM_REQUEST )
165
+
166
+ # Data Manager API compliance.
167
+ #
168
+ # The user and group IDs we're asked to run as.
169
+ # The files in the container project volume will be owned
170
+ # by this user and group. We must run as group 100.
171
+ # We use the supplied group ID and pass that into the container
172
+ # as the Kubernetes 'File System Group' (fsGroup).
173
+ # This should allow us to run and manipulate the files.
174
+ sc_run_as_user = material .get ("securityContext" , {}).get (
175
+ "runAsUser" , _DEFAULT_USER_ID
176
+ )
177
+ sc_run_as_group = material .get ("securityContext" , {}).get (
178
+ "runAsGroup" , _DEFAULT_GROUP_ID
179
+ )
180
+
181
+ # Project storage
182
+ project_claim_name = material .get ("project" , {}).get ("claimName" )
183
+ project_id = material .get ("project" , {}).get ("id" )
184
+
185
+ ingress_proxy_body_size = material .get (
186
+ "ingressProxyBodySize" , _DEFAULT_INGRESS_PROXY_BODY_SIZE
187
+ )
188
+
189
+ ingress_class = material .get ("ingressClass" , _DEFAULT_INGRESS_CLASS )
190
+ ingress_domain = material .get ("ingressDomain" , _DEFAULT_INGRESS_DOMAIN )
191
+ ingress_tls_secret = material .get ("ingressTlsSecret" , _DEFAULT_INGRESS_TLS_SECRET )
192
+ ingress_path = f"/{ name } "
193
+
145
194
# ConfigMaps
146
195
# ----------
147
196
@@ -169,11 +218,43 @@ def create(spec: Dict[str, Any], name: str, namespace: str, **_: Any) -> Dict[st
169
218
"data" : {"jupyter_notebook_config.json" : _NOTEBOOK_CONFIG % config_vars },
170
219
}
171
220
221
+ create_response = {
222
+ "notebook" : {
223
+ "url" : f"http://{ ingress_domain } { ingress_path } ?token={ token } " ,
224
+ "token" : token ,
225
+ "interface" : notebook_interface ,
226
+ },
227
+ "image" : image ,
228
+ "serviceAccountName" : service_account ,
229
+ "resources" : {
230
+ "requests" : {"memory" : memory_request },
231
+ "limits" : {"memory" : memory_limit },
232
+ },
233
+ "project" : {"claimName" : project_claim_name , "id" : project_id },
234
+ }
235
+
172
236
kopf .adopt (bp_cm_body )
173
237
kopf .adopt (startup_cm_body )
174
238
kopf .adopt (config_cm_body )
175
239
core_api = kubernetes .client .CoreV1Api ()
176
- core_api .create_namespaced_config_map (namespace , bp_cm_body )
240
+
241
+ # We currently see a number of 409 exceptions with the objects we create
242
+ # (with the reason 'Conflict'). As each one has a unique name
243
+ # we have to assume there is a serious underlying problem
244
+ # in kopf of kubernetes. For now, if the first object we create
245
+ # already exists let us assume they all do?
246
+ #
247
+ # Added as a work-wround for sc-
248
+ try :
249
+ core_api .create_namespaced_config_map (namespace , bp_cm_body )
250
+ except kubernetes .client .exceptions .ApiException as ex :
251
+ if ex .status != 409 or ex .reason != "Conflict" :
252
+ raise ex
253
+ # Warn, but ignore and return a valid 'create' response now.
254
+ logging .warning (
255
+ "Got ApiException [409/Conflict] creating BP ConfigMap. Ignoring [#10]"
256
+ )
257
+ return create_response
177
258
core_api .create_namespaced_config_map (namespace , startup_cm_body )
178
259
core_api .create_namespaced_config_map (namespace , config_cm_body )
179
260
@@ -184,46 +265,6 @@ def create(spec: Dict[str, Any], name: str, namespace: str, **_: Any) -> Dict[st
184
265
185
266
logging .info ("Creating Deployment %s..." , name )
186
267
187
- # All Data-Manager provided material
188
- # will be namespaced under the 'imDataManager' property
189
- material : Dict [str , Any ] = spec .get ("imDataManager" , {})
190
-
191
- notebook_interface = material .get ("notebook" , {}).get ("interface" , "lab" )
192
-
193
- image = material .get ("image" , _DEFAULT_IMAGE )
194
- image_parts = image .split (":" )
195
- image_tag = "latest" if len (image_parts ) == 1 else image_parts [1 ]
196
- image_pull_policy = (
197
- "Always" if image_tag .lower () in ["latest" , "stable" ] else "IfNotPresent"
198
- )
199
-
200
- service_account = material .get ("serviceAccountName" , _DEFAULT_SA )
201
-
202
- resources = material .get ("resources" , {})
203
- cpu_limit = resources .get ("limits" , {}).get ("cpu" , _DEFAULT_CPU_LIMIT )
204
- cpu_request = resources .get ("requests" , {}).get ("cpu" , _DEFAULT_CPU_REQUEST )
205
- memory_limit = resources .get ("limits" , {}).get ("memory" , _DEFAULT_MEM_LIMIT )
206
- memory_request = resources .get ("requests" , {}).get ("memory" , _DEFAULT_MEM_REQUEST )
207
-
208
- # Data Manager API compliance.
209
- #
210
- # The user and group IDs we're asked to run as.
211
- # The files in the container project volume will be owned
212
- # by this user and group. We must run as group 100.
213
- # We use the supplied group ID and pass that into the container
214
- # as the Kubernetes 'File System Group' (fsGroup).
215
- # This should allow us to run and manipulate the files.
216
- sc_run_as_user = material .get ("securityContext" , {}).get (
217
- "runAsUser" , _DEFAULT_USER_ID
218
- )
219
- sc_run_as_group = material .get ("securityContext" , {}).get (
220
- "runAsGroup" , _DEFAULT_GROUP_ID
221
- )
222
-
223
- # Project storage
224
- project_claim_name = material .get ("project" , {}).get ("claimName" )
225
- project_id = material .get ("project" , {}).get ("id" )
226
-
227
268
# Command is simply our custom start script,
228
269
# which is mounted at /usr/local/bin
229
270
command_items = ["bash" , "/usr/local/bin/start.sh" ]
@@ -368,15 +409,6 @@ def create(spec: Dict[str, Any], name: str, namespace: str, **_: Any) -> Dict[st
368
409
369
410
logging .info ("Creating Ingress %s..." , name )
370
411
371
- ingress_proxy_body_size = material .get (
372
- "ingressProxyBodySize" , _DEFAULT_INGRESS_PROXY_BODY_SIZE
373
- )
374
-
375
- ingress_class = material .get ("ingressClass" , _DEFAULT_INGRESS_CLASS )
376
- ingress_domain = material .get ("ingressDomain" , _DEFAULT_INGRESS_DOMAIN )
377
- ingress_tls_secret = material .get ("ingressTlsSecret" , _DEFAULT_INGRESS_TLS_SECRET )
378
- ingress_path = f"/{ name } "
379
-
380
412
ingress_body : Dict [Any , Any ] = {
381
413
"kind" : "Ingress" ,
382
414
"apiVersion" : "networking.k8s.io/v1" ,
@@ -424,17 +456,4 @@ def create(spec: Dict[str, Any], name: str, namespace: str, **_: Any) -> Dict[st
424
456
# Done
425
457
# ----
426
458
427
- return {
428
- "notebook" : {
429
- "url" : f"http://{ ingress_domain } { ingress_path } ?token={ token } " ,
430
- "token" : token ,
431
- "interface" : notebook_interface ,
432
- },
433
- "image" : image ,
434
- "serviceAccountName" : service_account ,
435
- "resources" : {
436
- "requests" : {"memory" : memory_request },
437
- "limits" : {"memory" : memory_limit },
438
- },
439
- "project" : {"claimName" : project_claim_name , "id" : project_id },
440
- }
459
+ return create_response
0 commit comments