jaseci 1.4.0.9__py3-none-any.whl → 1.4.0.11__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.

Potentially problematic release.


This version of jaseci might be problematic. Click here for more details.

Files changed (87) hide show
  1. jaseci/VERSION +1 -1
  2. jaseci/__init__.py +3 -0
  3. jaseci/actions/standard/elastic.py +3 -2
  4. jaseci/actions/standard/mail.py +3 -2
  5. jaseci/actions/standard/std.py +3 -2
  6. jaseci/actions/standard/stripe.py +3 -2
  7. jaseci/actions/standard/task.py +3 -5
  8. jaseci/actions/standard/tests/test_mail_lib.py +8 -7
  9. jaseci/actions/tests/test_std.py +4 -5
  10. jaseci/actor/walker.py +6 -3
  11. jaseci/api/config_api.py +3 -2
  12. jaseci/api/jac_api.py +2 -2
  13. jaseci/api/jsorc_api.py +60 -121
  14. jaseci/api/prometheus_api.py +14 -20
  15. jaseci/api/queue_api.py +9 -5
  16. jaseci/api/tests/test_global_api.py +3 -3
  17. jaseci/api/tests/test_logger_api.py +3 -3
  18. jaseci/api/user_api.py +3 -3
  19. jaseci/api/webhook_api.py +6 -4
  20. jaseci/attr/action.py +10 -4
  21. jaseci/element/master.py +2 -0
  22. jaseci/element/super_master.py +2 -0
  23. jaseci/hook/memory.py +3 -1
  24. jaseci/hook/redis.py +5 -4
  25. jaseci/jac/interpreter/interp.py +16 -4
  26. jaseci/jac/tests/test_book.py +2 -2
  27. jaseci/jsctl/jsctl.py +48 -15
  28. jaseci/jsctl/tests/test_jsctl.py +5 -0
  29. jaseci/jsorc.py +733 -0
  30. jaseci/jsorc_settings.py +184 -0
  31. jaseci/manifests/database.yaml +107 -0
  32. jaseci/manifests/elastic.yaml +5923 -0
  33. jaseci/manifests/prometheus.yaml +1273 -0
  34. jaseci/{svc/jsorc-backup/jaseci-redis.yaml → manifests/redis.yaml} +20 -0
  35. jaseci/svc/__init__.py +0 -25
  36. jaseci/svc/{elastic/elastic.py → elastic_svc.py} +5 -16
  37. jaseci/svc/kube_svc.py +240 -0
  38. jaseci/svc/{mail/mail.py → mail_svc.py} +14 -17
  39. jaseci/svc/{prometheus/prometheus.py → prome_svc.py} +5 -16
  40. jaseci/svc/{redis/redis.py → redis_svc.py} +14 -26
  41. jaseci/svc/{stripe/stripe.py → stripe_svc.py} +4 -7
  42. jaseci/svc/{task/task.py → task_svc.py} +27 -24
  43. jaseci/svc/{task/common.py → tasks.py} +287 -293
  44. jaseci/tests/jac_test_progs.py +21 -0
  45. jaseci/tests/test_core.py +14 -15
  46. jaseci/tests/test_jac.py +59 -60
  47. jaseci/tests/test_node.py +6 -13
  48. jaseci/tests/test_progs.py +74 -52
  49. jaseci/tests/test_stripe.py +6 -10
  50. jaseci/utils/actions/actions_manager.py +254 -0
  51. jaseci/{svc/actions_optimizer → utils/actions}/actions_optimizer.py +9 -19
  52. jaseci/utils/json_handler.py +2 -3
  53. jaseci/utils/test_core.py +4 -5
  54. jaseci/utils/utils.py +12 -0
  55. {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/METADATA +2 -1
  56. {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/RECORD +63 -80
  57. jaseci/svc/common.py +0 -763
  58. jaseci/svc/config.py +0 -9
  59. jaseci/svc/elastic/__init__.py +0 -3
  60. jaseci/svc/elastic/config.py +0 -8
  61. jaseci/svc/elastic/manifest.py +0 -1
  62. jaseci/svc/jsorc-backup/jsorc.py +0 -182
  63. jaseci/svc/jsorc-backup/promon/__init__.py +0 -0
  64. jaseci/svc/jsorc-backup/promon/promon.py +0 -202
  65. jaseci/svc/mail/__init__.py +0 -4
  66. jaseci/svc/mail/config.py +0 -25
  67. jaseci/svc/meta.py +0 -164
  68. jaseci/svc/postgres/__init__.py +0 -0
  69. jaseci/svc/postgres/manifest.py +0 -106
  70. jaseci/svc/prometheus/__init__.py +0 -5
  71. jaseci/svc/prometheus/config.py +0 -11
  72. jaseci/svc/prometheus/manifest.py +0 -1102
  73. jaseci/svc/redis/__init__.py +0 -5
  74. jaseci/svc/redis/config.py +0 -10
  75. jaseci/svc/redis/manifest.py +0 -65
  76. jaseci/svc/state.py +0 -17
  77. jaseci/svc/stripe/__init__.py +0 -3
  78. jaseci/svc/stripe/config.py +0 -7
  79. jaseci/svc/task/__init__.py +0 -5
  80. jaseci/svc/task/config.py +0 -17
  81. /jaseci/{svc/actions_optimizer → manifests}/__init__.py +0 -0
  82. /jaseci/{svc/jsorc-backup → utils/actions}/__init__.py +0 -0
  83. /jaseci/{svc/actions_optimizer → utils/actions}/actions_state.py +0 -0
  84. {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/LICENSE +0 -0
  85. {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/WHEEL +0 -0
  86. {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/entry_points.txt +0 -0
  87. {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/top_level.txt +0 -0
jaseci/jsorc.py ADDED
@@ -0,0 +1,733 @@
1
+ import signal
2
+ import psycopg2
3
+
4
+ from enum import Enum
5
+ from time import sleep
6
+ from copy import deepcopy
7
+ from datetime import datetime
8
+ from typing import TypeVar, Any, Union
9
+
10
+ from .utils.utils import logger
11
+ from .jsorc_settings import JsOrcSettings
12
+
13
+ from kubernetes.client.rest import ApiException
14
+ from multiprocessing import Process, current_process
15
+
16
+ # For future use
17
+ # from concurrent.futures import ThreadPoolExecutor
18
+ # from os import cpu_count
19
+
20
+ T = TypeVar("T")
21
+
22
+
23
+ class State(Enum):
24
+ FAILED = -1
25
+ NOT_STARTED = 0
26
+ STARTED = 1
27
+ RUNNING = 2
28
+
29
+ def is_ready(self):
30
+ return self == State.NOT_STARTED
31
+
32
+ def is_running(self):
33
+ return self == State.RUNNING
34
+
35
+ def has_failed(self):
36
+ return self == State.FAILED
37
+
38
+
39
+ class JsOrc:
40
+ #######################################################################################################
41
+ # INNER CLASS #
42
+ #######################################################################################################
43
+
44
+ class CommonService:
45
+ ###################################################
46
+ # PROPERTIES #
47
+ ###################################################
48
+
49
+ # ------------------- DAEMON -------------------- #
50
+
51
+ _daemon = {}
52
+
53
+ @property
54
+ def daemon(self):
55
+ return __class__._daemon
56
+
57
+ def __init__(self, config: dict, manifest: dict):
58
+ self.app = None
59
+ self.error = None
60
+ self.state = State.NOT_STARTED
61
+
62
+ # ------------------- CONFIG -------------------- #
63
+
64
+ self.config = config
65
+ self.enabled = config.pop("enabled", False)
66
+ self.quiet = config.pop("quiet", False)
67
+ self.automated = config.pop("automated", False)
68
+
69
+ # ------------------ MANIFEST ------------------- #
70
+
71
+ self.manifest = manifest
72
+ self.manifest_meta = {
73
+ "__OLD_CONFIG__": manifest.pop("__OLD_CONFIG__", {}),
74
+ "__UNSAFE_PARAPHRASE__": manifest.pop("__UNSAFE_PARAPHRASE__", ""),
75
+ }
76
+
77
+ self.start()
78
+
79
+ ###################################################
80
+ # BUILDER #
81
+ ###################################################
82
+
83
+ def start(self):
84
+ try:
85
+ if self.enabled and self.is_ready():
86
+ self.state = State.STARTED
87
+ self.run()
88
+ self.state = State.RUNNING
89
+ self.post_run()
90
+ except Exception as e:
91
+ if not (self.quiet):
92
+ logger.error(
93
+ f"Skipping {self.__class__.__name__} due to initialization "
94
+ f"failure!\n{e.__class__.__name__}: {e}"
95
+ )
96
+ self.failed(e)
97
+
98
+ return self
99
+
100
+ def run(self):
101
+ raise Exception(f"Not properly configured! Please override run method!")
102
+
103
+ def post_run(self):
104
+ pass
105
+
106
+ # ------------------- DAEMON -------------------- #
107
+
108
+ def spawn_daemon(self, **targets):
109
+ if current_process().name == "MainProcess":
110
+ for name, target in targets.items():
111
+ dae: Process = self.daemon.get(name)
112
+ if not dae or not dae.is_alive():
113
+ process = Process(target=target, daemon=True)
114
+ process.start()
115
+ self.daemon[name] = process
116
+
117
+ def terminate_daemon(self, *names):
118
+ for name in names:
119
+ dae: Process = self.daemon.pop(name, None)
120
+ if not (dae is None) and dae.is_alive():
121
+ logger.info(f"Terminating {name} ...")
122
+ dae.terminate()
123
+
124
+ ###################################################
125
+ # COMMONS #
126
+ ###################################################
127
+
128
+ def poke(self, cast: T = None, msg: str = None) -> Union[T, Any]:
129
+ if self.is_running():
130
+ return (
131
+ self
132
+ if cast and cast.__name__ == self.__class__.__name__
133
+ else self.app
134
+ )
135
+ raise Exception(
136
+ msg or f"{self.__class__.__name__} is disabled or not yet configured!"
137
+ )
138
+
139
+ def is_ready(self):
140
+ return self.state.is_ready() and self.app is None
141
+
142
+ def is_running(self):
143
+ return self.state.is_running() and not (self.app is None)
144
+
145
+ def has_failed(self):
146
+ return self.state.has_failed()
147
+
148
+ def failed(self, error: Exception = None):
149
+ self.app = None
150
+ self.state = State.FAILED
151
+ self.error = error
152
+
153
+ @classmethod
154
+ def proxy(cls):
155
+ return cls({}, {})
156
+
157
+ # ---------------- PROXY EVENTS ----------------- #
158
+
159
+ def on_delete(self):
160
+ pass
161
+
162
+ # ------------------- EVENTS -------------------- #
163
+
164
+ def __del__(self):
165
+ self.on_delete()
166
+
167
+ def __getstate__(self):
168
+ return {}
169
+
170
+ def __setstate__(self, ignored):
171
+ # for build on pickle load
172
+ self.state = State.FAILED
173
+ del self
174
+
175
+ #######################################################################################################
176
+ # JSORC CLASS #
177
+ #######################################################################################################
178
+
179
+ # ----------------- REFERENCE ----------------- #
180
+
181
+ _contexts = {}
182
+ _context_instances = {}
183
+
184
+ _services = {}
185
+ _service_instances = {}
186
+
187
+ _repositories = {}
188
+
189
+ # ----------------- SETTINGS ----------------- #
190
+
191
+ _use_proxy = False
192
+ _settings = JsOrcSettings
193
+
194
+ # For future use
195
+ # _executor = ThreadPoolExecutor(
196
+ # min(4, int((cpu_count() or 2) / 4) + 1)
197
+ # )
198
+
199
+ # ------------------ COMMONS ------------------ #
200
+
201
+ _backoff_interval = 10
202
+ _running_interval = 0
203
+ __running__ = False
204
+ __proxy__ = CommonService.proxy()
205
+
206
+ # ------------------- REGEN ------------------- #
207
+
208
+ _regeneration_queues = []
209
+ _regenerating = False
210
+ _has_db = False
211
+
212
+ @staticmethod
213
+ def push(name: str, target: dict, entry: dict):
214
+ if name not in target:
215
+ target[name] = [entry]
216
+ else:
217
+ target[name] = sorted(
218
+ target[name] + [entry],
219
+ key=lambda item: (-item["priority"], -item["date_added"]),
220
+ )
221
+
222
+ @classmethod
223
+ def run(cls):
224
+ if not cls.__running__:
225
+ cls.__running__ == True
226
+ config = cls.settings("JSORC_CONFIG")
227
+ if cls.db_check():
228
+ hook = cls.hook()
229
+ config = hook.service_glob("JSORC_CONFIG", config)
230
+ cls._backoff_interval = max(5, config.get("backoff_interval", 10))
231
+ cls._regeneration_queues = config.get("pre_loaded_services", [])
232
+ cls.push_interval(1)
233
+
234
+ @classmethod
235
+ def push_interval(cls, interval):
236
+ if cls._running_interval == 0:
237
+ cls._running_interval += 1
238
+ signal.alarm(interval)
239
+ else:
240
+ logger.info("Reusing current running interval...")
241
+
242
+ @classmethod
243
+ def kube(cls):
244
+ if not cls._kube:
245
+ raise Exception(f"Kubernetes is not yet ready!")
246
+ return cls._kube
247
+
248
+ #################################################
249
+ # HELPER #
250
+ #################################################
251
+
252
+ # ------------------ context ------------------ #
253
+
254
+ @classmethod
255
+ def get(cls, context: str, cast: T = None, *args, **kwargs) -> Union[T, Any]:
256
+ """
257
+ Get existing context instance or build a new one that will persists
258
+ ex: master
259
+
260
+ context: name of the context to be build
261
+ cast: to cast the return and allow code hinting
262
+ *arsgs: additional argument used to initialize context
263
+ **kwargs: additional keyword argument used to initialize context
264
+ """
265
+ if context not in cls._contexts:
266
+ raise Exception(f"Context {context} is not existing!")
267
+
268
+ if context not in cls._context_instances:
269
+ cls._context_instances[context] = cls.ctx(context, cast, *args, **kwargs)
270
+
271
+ return cls._context_instances[context]
272
+
273
+ @classmethod
274
+ def destroy(cls, context: str):
275
+ """
276
+ remove existing context instance
277
+ ex: master
278
+
279
+ context: name of the context to be build
280
+ """
281
+ if context not in cls._contexts:
282
+ raise Exception(f"Context {context} is not existing!")
283
+
284
+ if context in cls._context_instances:
285
+ del cls._context_instances[context]
286
+
287
+ @classmethod
288
+ def renew(cls, context: str, cast: T = None, *args, **kwargs) -> Union[T, Any]:
289
+ """
290
+ renew existing context instance or build a new one that will persists
291
+ ex: master
292
+
293
+ context: name of the context to be build
294
+ cast: to cast the return and allow code hinting
295
+ *arsgs: additional argument used to initialize context
296
+ **kwargs: additional keyword argument used to initialize context
297
+ """
298
+ cls.destroy(context)
299
+
300
+ cls._context_instances[context] = cls.ctx(context, cast, *args, **kwargs)
301
+ return cls._context_instances[context]
302
+
303
+ @classmethod
304
+ def ctx(cls, context: str, cast: T = None, *args, **kwargs) -> Union[T, Any]:
305
+ """
306
+ Build new instance of the context
307
+ ex: master
308
+
309
+ context: name of the context to be build
310
+ cast: to cast the return and allow code hinting
311
+ *arsgs: additional argument used to initialize context
312
+ **kwargs: additional keyword argument used to initialize context
313
+ """
314
+ if context not in cls._contexts:
315
+ raise Exception(f"Context {context} is not existing!")
316
+
317
+ # highest priority
318
+ context = cls._contexts[context][0]
319
+
320
+ return context["type"](*args, **kwargs)
321
+
322
+ @classmethod
323
+ def ctx_cls(cls, context: str):
324
+ """
325
+ Get the context class
326
+ """
327
+ if context not in cls._contexts:
328
+ return None
329
+
330
+ return cls._contexts[context][0]["type"]
331
+
332
+ @classmethod
333
+ def master(cls, cast: T = None, *args, **kwargs) -> Union[T, Any]:
334
+ """
335
+ Generate master instance
336
+ """
337
+ return cls.__gen_with_hook("master", cast, *args, **kwargs)
338
+
339
+ @classmethod
340
+ def super_master(cls, cast: T = None, *args, **kwargs) -> Union[T, Any]:
341
+ """
342
+ Generate super_master instance
343
+ """
344
+ return cls.__gen_with_hook("super_master", cast, *args, **kwargs)
345
+
346
+ @classmethod
347
+ def __gen_with_hook(cls, context: str, *args, **kwargs):
348
+ """
349
+ Common process on master and super_master
350
+ """
351
+ if not kwargs.get("h", None):
352
+ kwargs["h"] = cls.hook()
353
+
354
+ return cls.ctx(context, None, *args, **kwargs)
355
+
356
+ # ------------------ service ------------------ #
357
+
358
+ @classmethod
359
+ def _svc(cls, service: str) -> CommonService:
360
+ if service not in cls._services:
361
+ raise Exception(f"Service {service} is not existing!")
362
+
363
+ # highest priority
364
+ instance = cls._services[service][0]
365
+
366
+ config = cls.settings(instance["config"], cls.settings("DEFAULT_CONFIG"))
367
+ manifest = cls.settings(
368
+ instance["manifest"] or "DEFAULT_MANIFEST", cls.settings("DEFAULT_MANIFEST")
369
+ )
370
+
371
+ if cls.db_check():
372
+ hook = cls.hook(use_proxy=instance["proxy"])
373
+
374
+ config = hook.service_glob(instance["config"], config)
375
+
376
+ manifest = (
377
+ hook.service_glob(instance["manifest"], manifest)
378
+ if instance["manifest"]
379
+ else {}
380
+ )
381
+
382
+ instance: JsOrc.CommonService = instance["type"](config, manifest)
383
+
384
+ if instance.has_failed() and service not in cls._regeneration_queues:
385
+ cls._regeneration_queues.append(service)
386
+
387
+ return instance
388
+
389
+ @classmethod
390
+ def svc(cls, service: str, cast: T = None) -> Union[T, CommonService]:
391
+ """
392
+ Get service. Initialize when not yet existing.
393
+ ex: task
394
+
395
+ service: name of the service to be reference
396
+ cast: to cast the return and allow code hinting
397
+ """
398
+ if service not in cls._services:
399
+ raise Exception(f"Service {service} is not existing!")
400
+
401
+ if service not in cls._service_instances:
402
+ if cls._use_proxy and cls._services[service][0]["proxy"]:
403
+ return cls.__proxy__
404
+ cls._service_instances[service] = cls._svc(service)
405
+
406
+ return cls._service_instances[service]
407
+
408
+ @classmethod
409
+ def svc_reset(cls, service, cast: T = None) -> Union[T, CommonService]:
410
+ """
411
+ Service reset now deletes the actual instance and rebuild it
412
+ """
413
+ if service in cls._service_instances:
414
+ instance = cls._service_instances.pop(service)
415
+ del instance
416
+ return cls.svc(service)
417
+
418
+ # ---------------- repository ----------------- #
419
+
420
+ @classmethod
421
+ def src(cls, repository: str, cast: T = None) -> Union[T, Any]:
422
+ """
423
+ Initialize datasource class (repository)
424
+ ex: hook
425
+
426
+ repository: name of the repository to be reference
427
+ cast: to cast the return and allow code hinting
428
+ """
429
+ if repository not in cls._repositories:
430
+ raise Exception(f"Repository {repository} is not existing!")
431
+
432
+ # highest priority
433
+ repository = cls._repositories[repository][0]
434
+
435
+ return repository["type"]()
436
+
437
+ @classmethod
438
+ def hook(cls, cast: T = None, use_proxy: bool = False) -> Union[T, Any]:
439
+ """
440
+ Generate hook repository instance
441
+ """
442
+ cls._use_proxy = use_proxy
443
+ hook = cls.src("hook")
444
+ cls._use_proxy = False
445
+ return hook
446
+
447
+ #################################################
448
+ # DECORATORS #
449
+ #################################################
450
+
451
+ @classmethod
452
+ def service(
453
+ cls,
454
+ name: str = None,
455
+ config: str = None,
456
+ manifest: str = None,
457
+ priority: int = 0,
458
+ proxy: bool = False,
459
+ ):
460
+ """
461
+ Save the class in services options
462
+ name: name to be used for reference
463
+ config: config name from datasource
464
+ manifest: manifest name from datasource
465
+ priority: duplicate name will use the highest priority
466
+ proxy: allow proxy service
467
+ """
468
+
469
+ def decorator(service: T) -> T:
470
+ cls.push(
471
+ name=name or service.__name__,
472
+ target=cls._services,
473
+ entry={
474
+ "type": service,
475
+ "config": config or f"{name.upper()}_CONFIG",
476
+ "manifest": manifest,
477
+ "priority": priority,
478
+ "proxy": proxy,
479
+ "date_added": int(datetime.utcnow().timestamp() * 1000),
480
+ },
481
+ )
482
+ return service
483
+
484
+ return decorator
485
+
486
+ @classmethod
487
+ def repository(cls, name: str = None, priority: int = 0):
488
+ """
489
+ Save the class in repositories options
490
+ name: name to be used for reference
491
+ priority: duplicate name will use the highest priority
492
+ """
493
+
494
+ def decorator(repository: T) -> T:
495
+ cls.push(
496
+ name=name or repository.__name__,
497
+ target=cls._repositories,
498
+ entry={
499
+ "type": repository,
500
+ "priority": priority,
501
+ "date_added": int(datetime.utcnow().timestamp() * 1000),
502
+ },
503
+ )
504
+ return repository
505
+
506
+ return decorator
507
+
508
+ @classmethod
509
+ def context(cls, name: str = None, priority: int = 0):
510
+ """
511
+ Save the class in contexts options
512
+ name: name to be used for reference
513
+ priority: duplicate name will use the highest priority
514
+ """
515
+
516
+ def decorator(context: T) -> T:
517
+ cls.push(
518
+ name=name or context.__name__,
519
+ target=cls._contexts,
520
+ entry={
521
+ "type": context,
522
+ "priority": priority,
523
+ "date_added": int(datetime.utcnow().timestamp() * 1000),
524
+ },
525
+ )
526
+ return context
527
+
528
+ return decorator
529
+
530
+ @classmethod
531
+ def inject(cls, contexts: list = [], services: list = [], repositories: list = []):
532
+ """
533
+ Allow to inject instance on specific method/class
534
+ contexts: list of context name to inject
535
+ - can use tuple per entry (name, alias) instead of string
536
+ services: list of service name to inject
537
+ - can use tuple per entry (name, alias) instead of string
538
+ repositories: list of service name to inject
539
+ - can use tuple per entry (name, alias) instead of string
540
+ """
541
+
542
+ def decorator(callable):
543
+ def argument_handler(*args, **kwargs):
544
+ _instances = {}
545
+
546
+ for context in contexts:
547
+ if isinstance(context, tuple):
548
+ _instances[context[1]] = cls.ctx(context[0])
549
+ else:
550
+ _instances[context] = cls.ctx(context)
551
+ for repository in repositories:
552
+ if isinstance(repository, tuple):
553
+ _instances[repository[1]] = cls.src(repository[0])
554
+ else:
555
+ _instances[repository] = cls.src(repository)
556
+ for service in services:
557
+ if isinstance(service, tuple):
558
+ _instances[service[1]] = cls.svc(service[0])
559
+ else:
560
+ _instances[service] = cls.svc(service)
561
+
562
+ kwargs.update(_instances)
563
+ callable(*args, **kwargs)
564
+
565
+ return argument_handler
566
+
567
+ return decorator
568
+
569
+ @classmethod
570
+ def settings(cls, name: str, default: T = None) -> Union[T, Any]:
571
+ return getattr(cls._settings, name, default)
572
+
573
+ #################################################
574
+ # AUTOMATION #
575
+ #################################################
576
+
577
+ @classmethod
578
+ def regenerate(cls):
579
+ if not cls._regenerating:
580
+ cls._regenerating = True
581
+ from jaseci.svc.kube_svc import KubeService
582
+
583
+ if cls._has_db:
584
+ from jaseci.utils.actions.actions_manager import ActionManager
585
+
586
+ while cls._regeneration_queues:
587
+ regeneration_queue = cls._regeneration_queues.pop(0)
588
+ service = cls.svc(regeneration_queue)
589
+ kube = cls.svc("kube", KubeService)
590
+
591
+ if (
592
+ not service.is_running()
593
+ and service.enabled
594
+ and service.automated
595
+ ):
596
+ if service.manifest and kube.is_running():
597
+ pod_name = ""
598
+ old_config_map = deepcopy(
599
+ service.manifest_meta.get("__OLD_CONFIG__", {})
600
+ )
601
+ unsafe_paraphrase = service.manifest_meta.get(
602
+ "__UNSAFE_PARAPHRASE__", ""
603
+ )
604
+ for kind, confs in service.manifest.items():
605
+ for conf in confs:
606
+ name = conf["metadata"]["name"]
607
+ _confs = old_config_map.get(kind, {})
608
+ if name in _confs.keys():
609
+ _confs.pop(name)
610
+
611
+ if kind == "Service":
612
+ pod_name = name
613
+ res = kube.read(
614
+ kind,
615
+ name,
616
+ namespace=kube.resolve_namespace(conf),
617
+ )
618
+ if (
619
+ hasattr(res, "status")
620
+ and res.status == 404
621
+ and conf
622
+ ):
623
+ kube.create(kind, name, conf)
624
+ elif (
625
+ not isinstance(res, ApiException)
626
+ and res.metadata
627
+ ):
628
+ if res.metadata.labels:
629
+ config_version = res.metadata.labels.get(
630
+ "config_version", 1
631
+ )
632
+ else:
633
+ config_version = 1
634
+
635
+ if config_version != conf.get("metadata").get(
636
+ "labels", {}
637
+ ).get("config_version", 1):
638
+ kube.patch(kind, name, conf)
639
+
640
+ if (
641
+ old_config_map
642
+ and type(old_config_map) is dict
643
+ and kind in old_config_map
644
+ and name in old_config_map[kind].keys()
645
+ ):
646
+ old_config_map.get(kind, {}).pop(name)
647
+
648
+ for to_be_removed in old_config_map.get(
649
+ kind, {}
650
+ ).keys():
651
+ res = kube.read(
652
+ kind,
653
+ to_be_removed,
654
+ namespace=kube.resolve_namespace(
655
+ old_config_map["kind"][to_be_removed]
656
+ ),
657
+ )
658
+ if (
659
+ not isinstance(res, ApiException)
660
+ and res.metadata
661
+ ):
662
+ if kind not in cls.settings(
663
+ "UNSAFE_KINDS"
664
+ ) or unsafe_paraphrase == cls.settings(
665
+ "UNSAFE_PARAPHRASE"
666
+ ):
667
+ kube.delete(kind, to_be_removed)
668
+ else:
669
+ logger.info(
670
+ f"You don't have permission to delete `{kind}` for `{to_be_removed}` with namespace `{kube.namespace}`!"
671
+ )
672
+
673
+ if kube.is_pod_running(pod_name):
674
+ logger.info(
675
+ f"Pod state is running. Trying to Restart {regeneration_queue}..."
676
+ )
677
+ cls.svc_reset(regeneration_queue)
678
+ else:
679
+ cls._regeneration_queues.append(regeneration_queue)
680
+ else:
681
+ cls.svc_reset(regeneration_queue)
682
+ sleep(1)
683
+
684
+ action_manager = cls.get("action_manager", ActionManager)
685
+ action_manager.optimize(jsorc_interval=cls._backoff_interval)
686
+ action_manager.record_system_state()
687
+ else:
688
+ kube = cls.svc("kube", KubeService)
689
+ dbrc = cls.settings("DB_REGEN_CONFIG")
690
+ while not cls._has_db or not kube.terminate_jaseci(dbrc["pod"]):
691
+ for kind, confs in cls.settings("DB_REGEN_MANIFEST", {}).items():
692
+ for conf in confs:
693
+ name = conf["metadata"]["name"]
694
+ res = kube.read(kind, name)
695
+ if hasattr(res, "status") and res.status == 404 and conf:
696
+ kube.create(kind, name, conf)
697
+ sleep(1)
698
+ cls.db_check()
699
+
700
+ cls._regenerating = False
701
+
702
+ @classmethod
703
+ def db_check(cls):
704
+ if not cls._has_db:
705
+ try:
706
+ dbrc = cls.settings("DB_REGEN_CONFIG")
707
+ if dbrc["enabled"]:
708
+ connection = psycopg2.connect(
709
+ host=dbrc["host"],
710
+ dbname=dbrc["db"],
711
+ user=dbrc["user"],
712
+ password=dbrc["password"],
713
+ port=dbrc["port"],
714
+ )
715
+ connection.close()
716
+ cls._has_db = True
717
+ except Exception:
718
+ cls._has_db = False
719
+ return cls._has_db
720
+
721
+
722
+ def interval_check(signum, frame):
723
+ JsOrc.regenerate()
724
+ logger.info(
725
+ f"Backing off for {JsOrc._backoff_interval} seconds before the next interval check..."
726
+ )
727
+
728
+ # wait interval_check to be finished before decrement
729
+ JsOrc._running_interval -= 1
730
+ JsOrc.push_interval(JsOrc._backoff_interval)
731
+
732
+
733
+ signal.signal(signal.SIGALRM, interval_check)