crossplane-function-pythonic 0.6.2__py3-none-any.whl → 0.6.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,9 +7,10 @@ import logging
7
7
  import sys
8
8
 
9
9
  import grpc
10
+
11
+ from .. import pythonic
10
12
  from .proto.v1 import run_function_pb2 as fnv1
11
13
  from .proto.v1 import run_function_pb2_grpc as grpcv1
12
- from .. import pythonic
13
14
 
14
15
  logger = logging.getLogger(__name__)
15
16
 
@@ -29,7 +30,7 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
29
30
  module = module[:ix]
30
31
  if module in sys.modules:
31
32
  del sys.modules[module]
32
- ix = module.rfind('.')
33
+ ix = module.rfind(".")
33
34
  importlib.invalidate_caches()
34
35
  self.clazzes.clear()
35
36
 
@@ -38,82 +39,117 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
38
39
  ) -> fnv1.RunFunctionResponse:
39
40
  try:
40
41
  return await self.run_function(request)
41
- except Exception as e:
42
- return self.fatal(request, logger, 'RunFunction', e)
42
+ except Exception as e: # noqa: BLE001
43
+ return self.fatal(request, logger, "RunFunction", e)
43
44
 
44
45
  async def run_function(self, request):
45
46
  composite = request.observed.composite.resource
46
47
  if composite and composite.fields:
47
- name = list(reversed(composite['apiVersion'].split('/')[0].split('.')))
48
- name.append(composite['kind'])
49
- name.append(composite['metadata']['name'])
50
- logger = logging.getLogger('.'.join(name))
51
- if 'inlined' in request.input and request.input['inlined']:
52
- if 'spec' not in composite or request.input['inlined'] not in composite['spec']:
53
- return self.fatal(request, logger, f"Missing inlined spec.{request.input['inlined']}")
54
- composite = composite['spec'][request.input['inlined']]
48
+ name = list(reversed(composite["apiVersion"].split("/")[0].split(".")))
49
+ name.append(composite["kind"])
50
+ name.append(composite["metadata"]["name"])
51
+ logger = logging.getLogger(".".join(name))
52
+ if "inlined" in request.input and request.input["inlined"]: # noqa: RUF019
53
+ if (
54
+ "spec" not in composite
55
+ or request.input["inlined"] not in composite["spec"]
56
+ ):
57
+ return self.fatal(
58
+ request,
59
+ logger,
60
+ f"Missing inlined spec.{request.input['inlined']}",
61
+ )
62
+ composite = composite["spec"][request.input["inlined"]]
55
63
  else:
56
- if 'composite' not in request.input:
57
- return self.fatal(request, logger, 'Missing composition input "composite"')
58
- composite = request.input['composite']
64
+ if "composite" not in request.input:
65
+ return self.fatal(
66
+ request, logger, 'Missing composition input "composite"'
67
+ )
68
+ composite = request.input["composite"]
59
69
  operation = False
60
70
  else:
61
- logger = logging.getLogger('operation')
62
- if 'composite' not in request.input:
63
- return self.fatal(request, logger, 'Missing operation input "composite"')
64
- composite = request.input['composite']
71
+ logger = logging.getLogger("operation")
72
+ if "composite" not in request.input:
73
+ return self.fatal(
74
+ request, logger, 'Missing operation input "composite"'
75
+ )
76
+ composite = request.input["composite"]
65
77
  operation = True
66
78
 
67
79
  # Ideally this is something the Function API provides
68
- if 'step' in request.input:
69
- step = request.input['step']
80
+ if "step" in request.input:
81
+ step = request.input["step"]
70
82
  else:
71
83
  step = str(hash(composite))
72
84
 
73
85
  clazz = self.clazzes.get(composite)
74
86
  if not clazz:
75
- if '\n' in composite:
87
+ if "\n" in composite:
76
88
  module = Module()
77
89
  try:
78
- exec(composite, module.__dict__)
79
- except Exception as e:
80
- return self.fatal(request, logger, 'Exec', e)
90
+ exec(composite, module.__dict__) # noqa: S102
91
+ except Exception as e: # noqa: BLE001
92
+ return self.fatal(request, logger, "Exec", e)
81
93
  for field in dir(module):
82
94
  value = getattr(module, field)
83
- if inspect.isclass(value) and issubclass(value, pythonic.BaseComposite) and value != pythonic.BaseComposite:
95
+ if (
96
+ inspect.isclass(value)
97
+ and issubclass(value, pythonic.BaseComposite)
98
+ and value != pythonic.BaseComposite
99
+ ):
84
100
  if clazz:
85
- return self.fatal(request, logger, 'Composite script has multiple BaseComposite classes')
101
+ return self.fatal(
102
+ request,
103
+ logger,
104
+ "Composite script has multiple BaseComposite classes",
105
+ )
86
106
  clazz = value
87
107
  if not clazz:
88
- return self.fatal(request, logger, 'Composite script does not have a BaseComposite class')
108
+ return self.fatal(
109
+ request,
110
+ logger,
111
+ "Composite script does not have a BaseComposite class",
112
+ )
89
113
  else:
90
- composite = composite.rsplit('.', 1)
114
+ composite = composite.rsplit(".", 1)
91
115
  if len(composite) == 1:
92
- return self.fatal(request, logger, f"Composite class name does not include module: {composite[0]}")
116
+ return self.fatal(
117
+ request,
118
+ logger,
119
+ f"Composite class name does not include module: {composite[0]}",
120
+ )
93
121
  try:
94
122
  module = importlib.import_module(composite[0])
95
- except Exception as e:
96
- return self.fatal(request, logger, 'Import module', e)
123
+ except Exception as e: # noqa: BLE001
124
+ return self.fatal(request, logger, "Import module", e)
97
125
  clazz = getattr(module, composite[1], None)
98
126
  if not clazz:
99
- return self.fatal(request, logger, f"{composite[0]} does not define: {composite[1]}")
100
- composite = '.'.join(composite)
127
+ return self.fatal(
128
+ request,
129
+ logger,
130
+ f"{composite[0]} does not define: {composite[1]}",
131
+ )
132
+ composite = ".".join(composite)
101
133
  if not inspect.isclass(clazz):
102
134
  return self.fatal(request, logger, f"{composite} is not a class")
103
135
  if not issubclass(clazz, pythonic.BaseComposite):
104
- return self.fatal(request, logger, f"{composite} is not a subclass of BaseComposite")
136
+ return self.fatal(
137
+ request,
138
+ logger,
139
+ f"{composite} is not a subclass of BaseComposite",
140
+ )
105
141
  self.clazzes[composite] = clazz
106
142
 
107
143
  if operation:
108
- if '\n' in composite:
144
+ if "\n" in composite:
109
145
  logger = logging.getLogger(f"operation.{clazz.__name__}")
110
146
  else:
111
147
  logger = logging.getLogger(f"operation.{composite}")
112
148
 
113
149
  try:
114
150
  composite = clazz(self.crossplane_v1, request, logger)
115
- except Exception as e:
116
- return self.fatal(request, logger, 'Instantiate', e)
151
+ except Exception as e: # noqa: BLE001
152
+ return self.fatal(request, logger, "Instantiate", e)
117
153
 
118
154
  step = composite.context._pythonic[step]
119
155
  iteration = int(step.iteration) + 1
@@ -121,18 +157,22 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
121
157
  composite.context.iteration = iteration
122
158
  if iteration == 1 and not logger.isEnabledFor(logging.DEBUG):
123
159
  if len(composite.context._pythonic) == 1:
124
- logger.info('Starting compose')
160
+ logger.info("Starting compose")
125
161
  else:
126
- logger.info(f"Starting compose, {ordinal(len(composite.context._pythonic))} step")
162
+ logger.info(
163
+ f"Starting compose, {ordinal(len(composite.context._pythonic))} step"
164
+ )
127
165
  else:
128
- logger.debug(f"Starting compose, {ordinal(len(composite.context._pythonic))} step, {ordinal(iteration)} pass")
166
+ logger.debug(
167
+ f"Starting compose, {ordinal(len(composite.context._pythonic))} step, {ordinal(iteration)} pass"
168
+ )
129
169
 
130
170
  try:
131
171
  result = composite.compose()
132
172
  if asyncio.iscoroutine(result):
133
173
  await result
134
- except Exception as e:
135
- return self.fatal(request, logger, 'Compose', e)
174
+ except Exception as e: # noqa: BLE001
175
+ return self.fatal(request, logger, "Compose", e)
136
176
 
137
177
  schemas = self.get_schemas(step, composite)
138
178
  requireds = self.get_requireds(step, composite)
@@ -146,19 +186,19 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
146
186
  self.process_unknowns(composite)
147
187
  # Perform auto ready on all resources.
148
188
  for name, resource in composite.resources:
149
- resource.ready
150
- logger.info('Completed compose')
189
+ resource.ready # noqa: B018
190
+ logger.info("Completed compose")
151
191
 
152
192
  return composite.response._message
153
193
 
154
194
  def fatal(self, request, logger, message, exception=None):
155
195
  if exception:
156
- message += ' exception'
196
+ message += " exception"
157
197
  logger.exception(message)
158
198
  m = str(exception)
159
199
  if not m:
160
200
  m = exception.__class__.__name__
161
- message += ': ' + m
201
+ message += ": " + m
162
202
  else:
163
203
  logger.error(message)
164
204
  return fnv1.RunFunctionResponse(
@@ -170,7 +210,7 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
170
210
  severity=fnv1.SEVERITY_FATAL,
171
211
  message=message,
172
212
  )
173
- ]
213
+ ],
174
214
  )
175
215
 
176
216
  def get_schemas(self, step, composite):
@@ -201,9 +241,9 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
201
241
 
202
242
  def process_usages(self, composite):
203
243
  if self.crossplane_v1:
204
- apiVersion = 'apiextensions.crossplane.io/v1beta1'
244
+ apiVersion = "apiextensions.crossplane.io/v1beta1"
205
245
  else:
206
- apiVersion = 'protection.crossplane.io/v1beta1'
246
+ apiVersion = "protection.crossplane.io/v1beta1"
207
247
  for _, resource in sorted(entry for entry in composite.resources):
208
248
  dependencies = resource.desired._getDependencies
209
249
  if dependencies:
@@ -216,34 +256,41 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
216
256
  resources = {}
217
257
  requireds = {}
218
258
  for destination, source in sorted(dependencies.items()):
219
- name = source.split('.')
220
- if (len(name) > 5 and
221
- name[0] in ('request', 'response') and
222
- name[1] in ('observed', 'desired') and
223
- name[2] == 'resources' and
224
- name[4] == 'resource'
259
+ name = source.split(".")
260
+ if (
261
+ len(name) > 5
262
+ and name[0] in ("request", "response")
263
+ and name[1] in ("observed", "desired")
264
+ and name[2] == "resources"
265
+ and name[4] == "resource"
225
266
  ):
226
267
  if name[3] not in resources:
227
268
  resources[name[3]] = []
228
- resources[name[3]].append(f"{'.'.join(destination.split('.')[5:])} = {'.'.join(name[5:])}")
229
- elif (len(name) > 5 and
230
- name[0] == 'request' and
231
- name[1] in ('required_resources', 'extra_resources') and
232
- name[3].startswith('items[') and name[3][-1] == ']' and
233
- name[4] == 'resource'
269
+ resources[name[3]].append(
270
+ f"{'.'.join(destination.split('.')[5:])} = {'.'.join(name[5:])}"
271
+ )
272
+ elif (
273
+ len(name) > 5
274
+ and name[0] == "request"
275
+ and name[1] in ("required_resources", "extra_resources")
276
+ and name[3].startswith("items[")
277
+ and name[3][-1] == "]"
278
+ and name[4] == "resource"
234
279
  ):
235
280
  key = (name[2], int(name[3][6:-1]))
236
281
  if key not in requireds:
237
282
  requireds[key] = []
238
- requireds[key].append(f"{'.'.join(destination.split('.')[5:])} = {'.'.join(name[5:])}")
283
+ requireds[key].append(
284
+ f"{'.'.join(destination.split('.')[5:])} = {'.'.join(name[5:])}"
285
+ )
239
286
  for name, dependencies in resources.items():
240
287
  source = composite.resources[name]
241
288
  name = [resource.name, str(source.kind)]
242
289
  if source.metadata.namespace:
243
290
  name.append(str(source.metadata.namespace))
244
291
  name.append(str(source.observed.metadata.name))
245
- usage = composite.resources['_'.join(name)](apiVersion, 'Usage')
246
- usage.spec.reason = '\n'.join(dependencies)
292
+ usage = composite.resources["_".join(name)](apiVersion, "Usage")
293
+ usage.spec.reason = "\n".join(dependencies)
247
294
  usage.spec.replayDeletion = True
248
295
  usage.spec.by.apiVersion = resource.apiVersion
249
296
  usage.spec.by.kind = resource.kind
@@ -253,22 +300,34 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
253
300
  usage.spec.of.resourceRef.name = source.observed.metadata.name
254
301
  if not self.crossplane_v1:
255
302
  if composite.metadata.namespace:
256
- if source.metadata.namespace and source.metadata.namespace != composite.metadata.namespace:
257
- usage.spec.of.resourceRef.namespace = source.metadata.namespace
303
+ if (
304
+ source.metadata.namespace
305
+ and source.metadata.namespace
306
+ != composite.metadata.namespace
307
+ ):
308
+ usage.spec.of.resourceRef.namespace = (
309
+ source.metadata.namespace
310
+ )
258
311
  elif resource.metadata.namespace:
259
312
  usage.metadata.namespace = resource.metadata.namespace
260
- if source.metadata.namespace and source.metadata.namespace != resource.metadata.namespace:
261
- usage.spec.of.resourceRef.namespace = source.metadata.namespace
313
+ if (
314
+ source.metadata.namespace
315
+ and source.metadata.namespace
316
+ != resource.metadata.namespace
317
+ ):
318
+ usage.spec.of.resourceRef.namespace = (
319
+ source.metadata.namespace
320
+ )
262
321
  else:
263
- usage.kind = 'ClusterUsage'
322
+ usage.kind = "ClusterUsage"
264
323
  for key, dependencies in requireds.items():
265
324
  source = composite.requireds[key[0]][key[1]]
266
325
  name = [resource.name, str(source.kind)]
267
326
  if source.metadata.namespace:
268
327
  name.append(str(source.metadata.namespace))
269
328
  name.append(str(source.metadata.name))
270
- usage = composite.resources['_'.join(name)](apiVersion, 'Usage')
271
- usage.spec.reason = '\n'.join(dependencies)
329
+ usage = composite.resources["_".join(name)](apiVersion, "Usage")
330
+ usage.spec.reason = "\n".join(dependencies)
272
331
  usage.spec.replayDeletion = True
273
332
  usage.spec.by.apiVersion = resource.apiVersion
274
333
  usage.spec.by.kind = resource.kind
@@ -278,14 +337,26 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
278
337
  usage.spec.of.resourceRef.name = source.observed.metadata.name
279
338
  if not self.crossplane_v1:
280
339
  if composite.metadata.namespace:
281
- if source.metadata.namespace and source.metadata.namespace != composite.metadata.namespace:
282
- usage.spec.of.resourceRef.namespace = source.metadata.namespace
340
+ if (
341
+ source.metadata.namespace
342
+ and source.metadata.namespace
343
+ != composite.metadata.namespace
344
+ ):
345
+ usage.spec.of.resourceRef.namespace = (
346
+ source.metadata.namespace
347
+ )
283
348
  elif resource.metadata.namespace:
284
349
  usage.metadata.namespace = resource.metadata.namespace
285
- if source.metadata.namespace and source.metadata.namespace != resource.metadata.namespace:
286
- usage.spec.of.resourceRef.namespace = source.metadata.namespace
350
+ if (
351
+ source.metadata.namespace
352
+ and source.metadata.namespace
353
+ != resource.metadata.namespace
354
+ ):
355
+ usage.spec.of.resourceRef.namespace = (
356
+ source.metadata.namespace
357
+ )
287
358
  else:
288
- usage.kind = 'ClusterUsage'
359
+ usage.kind = "ClusterUsage"
289
360
 
290
361
  def process_unknowns(self, composite):
291
362
  unknownResources = []
@@ -300,7 +371,9 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
300
371
  if resource.observed:
301
372
  warningResources.append(name)
302
373
  warning = True
303
- if resource.unknownsFatal or (resource.unknownsFatal is None and composite.unknownsFatal):
374
+ if resource.unknownsFatal or (
375
+ resource.unknownsFatal is None and composite.unknownsFatal
376
+ ):
304
377
  fatalResources.append(name)
305
378
  fatal = True
306
379
  if composite.logger.isEnabledFor(logging.DEBUG):
@@ -308,11 +381,17 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
308
381
  destination = self.trimFullName(destination)
309
382
  source = self.trimFullName(source)
310
383
  if fatal:
311
- composite.logger.error(f'Observed unknown: {destination} = {source}')
384
+ composite.logger.error(
385
+ f"Observed unknown: {destination} = {source}"
386
+ )
312
387
  elif warning:
313
- composite.logger.warning(f'Observed unknown: {destination} = {source}')
388
+ composite.logger.warning(
389
+ f"Observed unknown: {destination} = {source}"
390
+ )
314
391
  else:
315
- composite.logger.debug(f'Desired unknown: {destination} = {source}')
392
+ composite.logger.debug(
393
+ f"Desired unknown: {destination} = {source}"
394
+ )
316
395
  if resource.observed:
317
396
  resource.desired._patchUnknowns(resource.observed)
318
397
  elif self.renderUnknowns:
@@ -322,26 +401,26 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
322
401
 
323
402
  if fatalResources:
324
403
  level = composite.logger.error
325
- reason = 'FatalUnknowns'
404
+ reason = "FatalUnknowns"
326
405
  message = f"Observed resources with unknowns: {','.join(fatalResources)}"
327
406
  status = False
328
407
  result = composite.results.fatal
329
408
  elif warningResources:
330
409
  level = composite.logger.warning
331
- reason = 'ObservedUnknowns'
410
+ reason = "ObservedUnknowns"
332
411
  message = f"Observed resources with unknowns: {','.join(warningResources)}"
333
412
  status = False
334
413
  result = composite.results.warning
335
414
  elif unknownResources:
336
415
  level = composite.logger.info
337
- reason = 'DesiredUnknowns'
416
+ reason = "DesiredUnknowns"
338
417
  message = f"Desired resources with unknowns: {','.join(unknownResources)}"
339
418
  status = False
340
419
  result = composite.results.info
341
420
  else:
342
421
  level = None
343
- reason = 'AllComposed'
344
- message = 'All resources are composed'
422
+ reason = "AllComposed"
423
+ message = "All resources are composed"
345
424
  status = True
346
425
  result = None
347
426
  if not composite.logger.isEnabledFor(logging.DEBUG) and level:
@@ -351,23 +430,26 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
351
430
  result(reason, message)
352
431
 
353
432
  def trimFullName(self, name):
354
- name = name.split('.')
433
+ name = name.split(".")
355
434
  for values in (
356
- ('request', 'observed', 'composite', 'resource'),
357
- ('response', 'desired', 'composite', 'resource'),
358
- ('request', 'observed', 'resources', None, 'resource'),
359
- ('response', 'desired', 'resources', None, 'resource'),
360
- ('request', 'required_resources', None, 'items', None, 'resource'),
361
- ('request', 'extra_resources', None, 'items', None, 'resource'),
435
+ ("request", "observed", "composite", "resource"),
436
+ ("response", "desired", "composite", "resource"),
437
+ ("request", "observed", "resources", None, "resource"),
438
+ ("response", "desired", "resources", None, "resource"),
439
+ ("request", "required_resources", None, "items", None, "resource"),
440
+ ("request", "extra_resources", None, "items", None, "resource"),
362
441
  ):
363
442
  if len(values) < len(name):
364
443
  ix = 0
365
444
  for iv, value in enumerate(values):
366
- if value:
367
- if value != name[ix]:
368
- if not name[ix].startswith(f"{values[iv]}[") or iv+1 >= len(values) or values[iv+1]:
369
- break
370
- continue
445
+ if value and value != name[ix]:
446
+ if (
447
+ not name[ix].startswith(f"{value}[")
448
+ or iv + 1 >= len(values)
449
+ or values[iv + 1]
450
+ ):
451
+ break
452
+ continue
371
453
  ix += 1
372
454
  else:
373
455
  ix = 0
@@ -376,22 +458,22 @@ class FunctionRunner(grpcv1.FunctionRunnerService):
376
458
  if value == name[ix]:
377
459
  del name[ix]
378
460
  elif ix:
379
- name[ix-1] += name[ix][len(value):]
461
+ name[ix - 1] += name[ix][len(value) :]
380
462
  del name[ix]
381
463
  else:
382
- name[ix] = name[ix][len(value):]
464
+ name[ix] = name[ix][len(value) :]
383
465
  else:
384
466
  ix += 1
385
467
  break
386
- return '.'.join(name)
468
+ return ".".join(name)
387
469
 
388
470
 
389
471
  def ordinal(ix):
390
472
  ix = int(ix)
391
473
  if 11 <= (ix % 100) <= 13:
392
- suffix = 'th'
474
+ suffix = "th"
393
475
  else:
394
- suffix = ['th', 'st', 'nd', 'rd', 'th'][min(ix % 10, 4)]
476
+ suffix = ["th", "st", "nd", "rd", "th"][min(ix % 10, 4)]
395
477
  return str(ix) + suffix
396
478
 
397
479