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.
- jaseci/VERSION +1 -1
- jaseci/__init__.py +3 -0
- jaseci/actions/standard/elastic.py +3 -2
- jaseci/actions/standard/mail.py +3 -2
- jaseci/actions/standard/std.py +3 -2
- jaseci/actions/standard/stripe.py +3 -2
- jaseci/actions/standard/task.py +3 -5
- jaseci/actions/standard/tests/test_mail_lib.py +8 -7
- jaseci/actions/tests/test_std.py +4 -5
- jaseci/actor/walker.py +6 -3
- jaseci/api/config_api.py +3 -2
- jaseci/api/jac_api.py +2 -2
- jaseci/api/jsorc_api.py +60 -121
- jaseci/api/prometheus_api.py +14 -20
- jaseci/api/queue_api.py +9 -5
- jaseci/api/tests/test_global_api.py +3 -3
- jaseci/api/tests/test_logger_api.py +3 -3
- jaseci/api/user_api.py +3 -3
- jaseci/api/webhook_api.py +6 -4
- jaseci/attr/action.py +10 -4
- jaseci/element/master.py +2 -0
- jaseci/element/super_master.py +2 -0
- jaseci/hook/memory.py +3 -1
- jaseci/hook/redis.py +5 -4
- jaseci/jac/interpreter/interp.py +16 -4
- jaseci/jac/tests/test_book.py +2 -2
- jaseci/jsctl/jsctl.py +48 -15
- jaseci/jsctl/tests/test_jsctl.py +5 -0
- jaseci/jsorc.py +733 -0
- jaseci/jsorc_settings.py +184 -0
- jaseci/manifests/database.yaml +107 -0
- jaseci/manifests/elastic.yaml +5923 -0
- jaseci/manifests/prometheus.yaml +1273 -0
- jaseci/{svc/jsorc-backup/jaseci-redis.yaml → manifests/redis.yaml} +20 -0
- jaseci/svc/__init__.py +0 -25
- jaseci/svc/{elastic/elastic.py → elastic_svc.py} +5 -16
- jaseci/svc/kube_svc.py +240 -0
- jaseci/svc/{mail/mail.py → mail_svc.py} +14 -17
- jaseci/svc/{prometheus/prometheus.py → prome_svc.py} +5 -16
- jaseci/svc/{redis/redis.py → redis_svc.py} +14 -26
- jaseci/svc/{stripe/stripe.py → stripe_svc.py} +4 -7
- jaseci/svc/{task/task.py → task_svc.py} +27 -24
- jaseci/svc/{task/common.py → tasks.py} +287 -293
- jaseci/tests/jac_test_progs.py +21 -0
- jaseci/tests/test_core.py +14 -15
- jaseci/tests/test_jac.py +59 -60
- jaseci/tests/test_node.py +6 -13
- jaseci/tests/test_progs.py +74 -52
- jaseci/tests/test_stripe.py +6 -10
- jaseci/utils/actions/actions_manager.py +254 -0
- jaseci/{svc/actions_optimizer → utils/actions}/actions_optimizer.py +9 -19
- jaseci/utils/json_handler.py +2 -3
- jaseci/utils/test_core.py +4 -5
- jaseci/utils/utils.py +12 -0
- {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/METADATA +2 -1
- {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/RECORD +63 -80
- jaseci/svc/common.py +0 -763
- jaseci/svc/config.py +0 -9
- jaseci/svc/elastic/__init__.py +0 -3
- jaseci/svc/elastic/config.py +0 -8
- jaseci/svc/elastic/manifest.py +0 -1
- jaseci/svc/jsorc-backup/jsorc.py +0 -182
- jaseci/svc/jsorc-backup/promon/__init__.py +0 -0
- jaseci/svc/jsorc-backup/promon/promon.py +0 -202
- jaseci/svc/mail/__init__.py +0 -4
- jaseci/svc/mail/config.py +0 -25
- jaseci/svc/meta.py +0 -164
- jaseci/svc/postgres/__init__.py +0 -0
- jaseci/svc/postgres/manifest.py +0 -106
- jaseci/svc/prometheus/__init__.py +0 -5
- jaseci/svc/prometheus/config.py +0 -11
- jaseci/svc/prometheus/manifest.py +0 -1102
- jaseci/svc/redis/__init__.py +0 -5
- jaseci/svc/redis/config.py +0 -10
- jaseci/svc/redis/manifest.py +0 -65
- jaseci/svc/state.py +0 -17
- jaseci/svc/stripe/__init__.py +0 -3
- jaseci/svc/stripe/config.py +0 -7
- jaseci/svc/task/__init__.py +0 -5
- jaseci/svc/task/config.py +0 -17
- /jaseci/{svc/actions_optimizer → manifests}/__init__.py +0 -0
- /jaseci/{svc/jsorc-backup → utils/actions}/__init__.py +0 -0
- /jaseci/{svc/actions_optimizer → utils/actions}/actions_state.py +0 -0
- {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/LICENSE +0 -0
- {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/WHEEL +0 -0
- {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/entry_points.txt +0 -0
- {jaseci-1.4.0.9.dist-info → jaseci-1.4.0.11.dist-info}/top_level.txt +0 -0
jaseci/svc/common.py
DELETED
|
@@ -1,763 +0,0 @@
|
|
|
1
|
-
from copy import deepcopy
|
|
2
|
-
from multiprocessing import Process
|
|
3
|
-
|
|
4
|
-
from kubernetes import config as kubernetes_config
|
|
5
|
-
from kubernetes.client import ApiClient, CoreV1Api, AppsV1Api, RbacAuthorizationV1Api
|
|
6
|
-
from kubernetes.client.rest import ApiException
|
|
7
|
-
|
|
8
|
-
from jaseci.utils.utils import logger
|
|
9
|
-
from jaseci.actions.live_actions import load_action_config
|
|
10
|
-
from jaseci.svc.actions_optimizer.actions_optimizer import ActionsOptimizer
|
|
11
|
-
from .state import ServiceState as Ss
|
|
12
|
-
from .config import META_CONFIG, KUBERNETES_CONFIG
|
|
13
|
-
|
|
14
|
-
import time
|
|
15
|
-
import numpy as np
|
|
16
|
-
|
|
17
|
-
###################################################
|
|
18
|
-
# UNSAFE PARAMS #
|
|
19
|
-
###################################################
|
|
20
|
-
|
|
21
|
-
UNSAFE_PARAPHRASE = "I know what I'm doing!"
|
|
22
|
-
UNSAFE_KINDS = ["PersistentVolumeClaim"]
|
|
23
|
-
|
|
24
|
-
COMMON_ERROR = "Not properly configured!"
|
|
25
|
-
DEFAULT_CONFIG = {"enabled": False}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class CommonService:
|
|
29
|
-
_daemon = {}
|
|
30
|
-
|
|
31
|
-
def __init__(self, hook=None):
|
|
32
|
-
self.app = None
|
|
33
|
-
self.enabled = False
|
|
34
|
-
self.state = Ss.NOT_STARTED
|
|
35
|
-
self.quiet = True
|
|
36
|
-
self.build_settings(hook)
|
|
37
|
-
|
|
38
|
-
###################################################
|
|
39
|
-
# PROPERTIES #
|
|
40
|
-
###################################################
|
|
41
|
-
|
|
42
|
-
# ------------------- DAEMON -------------------- #
|
|
43
|
-
|
|
44
|
-
@property
|
|
45
|
-
def daemon(self):
|
|
46
|
-
return __class__._daemon
|
|
47
|
-
|
|
48
|
-
###################################################
|
|
49
|
-
# BUILDER #
|
|
50
|
-
###################################################
|
|
51
|
-
|
|
52
|
-
def start(self, hook=None):
|
|
53
|
-
try:
|
|
54
|
-
if self.enabled and self.is_ready():
|
|
55
|
-
self.state = Ss.STARTED
|
|
56
|
-
self.run(hook)
|
|
57
|
-
self.state = Ss.RUNNING
|
|
58
|
-
self.post_run(hook)
|
|
59
|
-
except Exception as e:
|
|
60
|
-
if not (self.quiet):
|
|
61
|
-
logger.error(
|
|
62
|
-
f"Skipping {self.__class__.__name__} due to initialization "
|
|
63
|
-
f"failure!\n{e.__class__.__name__}: {e}"
|
|
64
|
-
)
|
|
65
|
-
self.failed()
|
|
66
|
-
|
|
67
|
-
return self
|
|
68
|
-
|
|
69
|
-
def run(self, hook=None):
|
|
70
|
-
raise Exception(f"{COMMON_ERROR} Please override run method!")
|
|
71
|
-
|
|
72
|
-
def post_run(self, hook=None):
|
|
73
|
-
pass
|
|
74
|
-
|
|
75
|
-
###################################################
|
|
76
|
-
# COMMONS #
|
|
77
|
-
###################################################
|
|
78
|
-
|
|
79
|
-
def poke(self, msg: str = None):
|
|
80
|
-
if self.is_running():
|
|
81
|
-
return self.app
|
|
82
|
-
raise Exception(
|
|
83
|
-
msg or f"{self.__class__.__name__} is disabled or not yet configured!"
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
def is_ready(self):
|
|
87
|
-
return self.state.is_ready() and self.app is None
|
|
88
|
-
|
|
89
|
-
def is_running(self):
|
|
90
|
-
return self.state.is_running() and not (self.app is None)
|
|
91
|
-
|
|
92
|
-
def has_failed(self):
|
|
93
|
-
return self.state.has_failed()
|
|
94
|
-
|
|
95
|
-
def build_settings(self, hook) -> dict:
|
|
96
|
-
try:
|
|
97
|
-
self.manifest = self.build_manifest(hook)
|
|
98
|
-
self.manifest_meta = {}
|
|
99
|
-
if self.manifest:
|
|
100
|
-
self.manifest_meta["__OLD_CONFIG__"] = self.manifest.pop(
|
|
101
|
-
"__OLD_CONFIG__", {}
|
|
102
|
-
)
|
|
103
|
-
self.manifest_meta["__UNSAFE_PARAPHRASE__"] = self.manifest.pop(
|
|
104
|
-
"__UNSAFE_PARAPHRASE__", ""
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
config = self.build_config(hook)
|
|
108
|
-
self.enabled = config.pop("enabled", False)
|
|
109
|
-
self.quiet = config.pop("quiet", False)
|
|
110
|
-
self.config = config
|
|
111
|
-
except Exception:
|
|
112
|
-
logger.exception(f"Error loading settings for {self.__class__}")
|
|
113
|
-
self.config = DEFAULT_CONFIG
|
|
114
|
-
self.manifest = None
|
|
115
|
-
self.manifest_meta = {}
|
|
116
|
-
|
|
117
|
-
def build_config(self, hook) -> dict:
|
|
118
|
-
return DEFAULT_CONFIG
|
|
119
|
-
|
|
120
|
-
def build_manifest(self, hook) -> dict:
|
|
121
|
-
pass
|
|
122
|
-
|
|
123
|
-
# ------------------- DAEMON -------------------- #
|
|
124
|
-
|
|
125
|
-
def spawn_daemon(self, **targets):
|
|
126
|
-
for name, target in targets.items():
|
|
127
|
-
dae: Process = self.daemon.get(name)
|
|
128
|
-
if not dae or not dae.is_alive():
|
|
129
|
-
process = Process(target=target, daemon=True)
|
|
130
|
-
process.start()
|
|
131
|
-
self.daemon[name] = process
|
|
132
|
-
|
|
133
|
-
def terminate_daemon(self, *names):
|
|
134
|
-
for name in names:
|
|
135
|
-
dae: Process = self.daemon.pop(name, None)
|
|
136
|
-
if not (dae is None) and dae.is_alive():
|
|
137
|
-
logger.info(f"Terminating {name} ...")
|
|
138
|
-
dae.terminate()
|
|
139
|
-
|
|
140
|
-
###################################################
|
|
141
|
-
# CLEANER #
|
|
142
|
-
###################################################
|
|
143
|
-
|
|
144
|
-
def reset(self, hook, start=True):
|
|
145
|
-
self.app = None
|
|
146
|
-
self.state = Ss.NOT_STARTED
|
|
147
|
-
self.__init__(hook)
|
|
148
|
-
|
|
149
|
-
if start:
|
|
150
|
-
self.start(hook)
|
|
151
|
-
|
|
152
|
-
def failed(self):
|
|
153
|
-
self.app = None
|
|
154
|
-
self.state = Ss.FAILED
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
class ProxyService(CommonService):
|
|
158
|
-
def __init__(self):
|
|
159
|
-
super().__init__(__class__)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
class Kube:
|
|
163
|
-
def __init__(self, in_cluster: bool, config: dict):
|
|
164
|
-
if in_cluster:
|
|
165
|
-
kubernetes_config.load_incluster_config()
|
|
166
|
-
else:
|
|
167
|
-
kubernetes_config.load_kube_config()
|
|
168
|
-
|
|
169
|
-
self.client = ApiClient(config)
|
|
170
|
-
self.core = CoreV1Api(config)
|
|
171
|
-
self.api = AppsV1Api(self.client)
|
|
172
|
-
self.auth = RbacAuthorizationV1Api(self.client)
|
|
173
|
-
self.ping()
|
|
174
|
-
self.defaults()
|
|
175
|
-
|
|
176
|
-
def ping(self):
|
|
177
|
-
res = self.client.call_api("/readyz", "GET")
|
|
178
|
-
return res[1] == 200
|
|
179
|
-
|
|
180
|
-
def create(self, api, namespace, conf):
|
|
181
|
-
if api.startswith("ClusterRole"):
|
|
182
|
-
self.create_apis[api](body=conf)
|
|
183
|
-
else:
|
|
184
|
-
self.create_apis[api](namespace=namespace, body=conf)
|
|
185
|
-
|
|
186
|
-
def patch(self, api, name, namespace, conf):
|
|
187
|
-
if api.startswith("ClusterRole"):
|
|
188
|
-
self.patch_apis[api](name=name, body=conf)
|
|
189
|
-
else:
|
|
190
|
-
self.patch_apis[api](name=name, namespace=namespace, body=conf)
|
|
191
|
-
|
|
192
|
-
def read(self, api: str, name: str, namespace: str = None):
|
|
193
|
-
if api.startswith("ClusterRole"):
|
|
194
|
-
return self.read_apis[api](name=name)
|
|
195
|
-
else:
|
|
196
|
-
return self.read_apis[api](name=name, namespace=namespace)
|
|
197
|
-
|
|
198
|
-
def delete(self, api: str, name: str, namespace: str = None):
|
|
199
|
-
if api.startswith("ClusterRole"):
|
|
200
|
-
return self.delete_apis[api](name=name)
|
|
201
|
-
else:
|
|
202
|
-
return self.delete_apis[api](name=name, namespace=namespace)
|
|
203
|
-
|
|
204
|
-
def is_running(self, name: str, namespace: str):
|
|
205
|
-
try:
|
|
206
|
-
return (
|
|
207
|
-
self.core.list_namespaced_pod(
|
|
208
|
-
namespace=namespace, label_selector=f"pod={name}"
|
|
209
|
-
)
|
|
210
|
-
.items[0]
|
|
211
|
-
.status.phase
|
|
212
|
-
== "Running"
|
|
213
|
-
)
|
|
214
|
-
except Exception:
|
|
215
|
-
return False
|
|
216
|
-
|
|
217
|
-
def defaults(self):
|
|
218
|
-
self.create_apis = {
|
|
219
|
-
"Service": self.core.create_namespaced_service,
|
|
220
|
-
"Deployment": self.api.create_namespaced_deployment,
|
|
221
|
-
"ConfigMap": self.core.create_namespaced_config_map,
|
|
222
|
-
"ServiceAccount": self.core.create_namespaced_service_account,
|
|
223
|
-
"ClusterRole": self.auth.create_cluster_role,
|
|
224
|
-
"ClusterRoleBinding": self.auth.create_cluster_role_binding,
|
|
225
|
-
"Secret": self.core.create_namespaced_secret,
|
|
226
|
-
"PersistentVolumeClaim": (
|
|
227
|
-
self.core.create_namespaced_persistent_volume_claim
|
|
228
|
-
),
|
|
229
|
-
"DaemonSet": self.api.create_namespaced_daemon_set,
|
|
230
|
-
}
|
|
231
|
-
self.patch_apis = {
|
|
232
|
-
"Service": self.core.patch_namespaced_service,
|
|
233
|
-
"Deployment": self.api.patch_namespaced_deployment,
|
|
234
|
-
"ConfigMap": self.core.patch_namespaced_config_map,
|
|
235
|
-
"ServiceAccount": self.core.patch_namespaced_service_account,
|
|
236
|
-
"ClusterRole": self.auth.patch_cluster_role,
|
|
237
|
-
"ClusterRoleBinding": self.auth.patch_cluster_role_binding,
|
|
238
|
-
"Secret": self.core.patch_namespaced_secret,
|
|
239
|
-
"PersistentVolumeClaim": (
|
|
240
|
-
self.core.patch_namespaced_persistent_volume_claim
|
|
241
|
-
),
|
|
242
|
-
"DaemonSet": self.api.patch_namespaced_daemon_set,
|
|
243
|
-
}
|
|
244
|
-
self.delete_apis = {
|
|
245
|
-
"Service": self.core.delete_namespaced_service,
|
|
246
|
-
"Deployment": self.api.delete_namespaced_deployment,
|
|
247
|
-
"ConfigMap": self.core.delete_namespaced_config_map,
|
|
248
|
-
"ServiceAccount": self.core.delete_namespaced_service_account,
|
|
249
|
-
"ClusterRole": self.auth.delete_cluster_role,
|
|
250
|
-
"ClusterRoleBinding": self.auth.delete_cluster_role_binding,
|
|
251
|
-
"Secret": self.core.delete_namespaced_secret,
|
|
252
|
-
"PersistentVolumeClaim": (
|
|
253
|
-
self.core.delete_namespaced_persistent_volume_claim
|
|
254
|
-
),
|
|
255
|
-
"DaemonSet": self.api.delete_namespaced_daemon_set,
|
|
256
|
-
}
|
|
257
|
-
self.read_apis = {
|
|
258
|
-
"Service": self.core.read_namespaced_service,
|
|
259
|
-
"Endpoints": self.core.read_namespaced_endpoints,
|
|
260
|
-
"Deployment": self.api.read_namespaced_deployment,
|
|
261
|
-
"ConfigMap": self.core.read_namespaced_config_map,
|
|
262
|
-
"ServiceAccount": self.core.read_namespaced_service_account,
|
|
263
|
-
"ClusterRole": self.auth.read_cluster_role,
|
|
264
|
-
"ClusterRoleBinding": self.auth.read_cluster_role_binding,
|
|
265
|
-
"Secret": self.core.read_namespaced_secret,
|
|
266
|
-
"PersistentVolumeClaim": self.core.read_namespaced_persistent_volume_claim,
|
|
267
|
-
"DaemonSet": self.api.read_namespaced_daemon_set,
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
class JsOrc:
|
|
272
|
-
def __init__(self, meta):
|
|
273
|
-
self.automated = False
|
|
274
|
-
self.meta = meta
|
|
275
|
-
self.services = {}
|
|
276
|
-
self.background = {}
|
|
277
|
-
self.context = {}
|
|
278
|
-
self.benchmark = {
|
|
279
|
-
"jsorc": {"active": False, "requests": {}},
|
|
280
|
-
"actions_optimizer": {"active": False, "requests": {}},
|
|
281
|
-
}
|
|
282
|
-
self.actions_history = {"active": False, "history": []}
|
|
283
|
-
self.actions_calls = {}
|
|
284
|
-
self.system_states = {"active": False, "states": []}
|
|
285
|
-
self.actions_optimizer = ActionsOptimizer(
|
|
286
|
-
benchmark=self.benchmark["actions_optimizer"],
|
|
287
|
-
actions_history=self.actions_history,
|
|
288
|
-
actions_calls=self.actions_calls,
|
|
289
|
-
)
|
|
290
|
-
|
|
291
|
-
###################################################
|
|
292
|
-
# BUILDER #
|
|
293
|
-
###################################################
|
|
294
|
-
|
|
295
|
-
def build(self):
|
|
296
|
-
try:
|
|
297
|
-
hook = self.build_context("hook")
|
|
298
|
-
config = hook.service_glob("META_CONFIG", META_CONFIG)
|
|
299
|
-
if config.pop("automation", False):
|
|
300
|
-
self.kubernetes = Kube(**config.pop("kubernetes", KUBERNETES_CONFIG))
|
|
301
|
-
self.actions_optimizer.kube = self.kubernetes
|
|
302
|
-
self.prometheus = self.meta.get_service("promon", hook)
|
|
303
|
-
self.backoff_interval = config.pop("backoff_interval", 10)
|
|
304
|
-
self.namespace = config.pop("namespace", "default")
|
|
305
|
-
self.actions_optimizer.namespace = self.namespace
|
|
306
|
-
self.keep_alive = config.pop(
|
|
307
|
-
"keep_alive", ["promon", "redis", "task", "mail"]
|
|
308
|
-
)
|
|
309
|
-
self.automated = True
|
|
310
|
-
else:
|
|
311
|
-
self.automated = False
|
|
312
|
-
except Exception:
|
|
313
|
-
self.automated = False
|
|
314
|
-
|
|
315
|
-
def interval_check(self):
|
|
316
|
-
hook = self.meta.build_hook()
|
|
317
|
-
for svc in self.keep_alive:
|
|
318
|
-
try:
|
|
319
|
-
self.check(self.namespace, svc, hook)
|
|
320
|
-
except Exception as e:
|
|
321
|
-
logger.exception(
|
|
322
|
-
f"Error checking {svc} !\n" f"{e.__class__.__name__}: {e}"
|
|
323
|
-
)
|
|
324
|
-
self.optimize(jsorc_interval=self.backoff_interval)
|
|
325
|
-
self.record_system_state()
|
|
326
|
-
|
|
327
|
-
###################################################
|
|
328
|
-
# KUBERNETES #
|
|
329
|
-
###################################################
|
|
330
|
-
|
|
331
|
-
def in_cluster(self):
|
|
332
|
-
"""
|
|
333
|
-
Check if JSORC/Jaseci is running in a kubernetes cluster
|
|
334
|
-
"""
|
|
335
|
-
try:
|
|
336
|
-
if not hasattr(self, "kubernetes"):
|
|
337
|
-
return False
|
|
338
|
-
return self.kubernetes.ping()
|
|
339
|
-
except ApiException as e:
|
|
340
|
-
logger.info(f"Kubernetes cluster environment check failed: {e}")
|
|
341
|
-
return False
|
|
342
|
-
|
|
343
|
-
def create(self, kind: str, name: str, namespace: str, conf: dict):
|
|
344
|
-
try:
|
|
345
|
-
logger.info(f"Creating {kind} for `{name}` with namespace `{namespace}`")
|
|
346
|
-
self.kubernetes.create(kind, namespace, conf)
|
|
347
|
-
except ApiException as e:
|
|
348
|
-
logger.error(
|
|
349
|
-
f"Error creating {kind} for `{name}` with namespace `{namespace}` -- {e}"
|
|
350
|
-
)
|
|
351
|
-
|
|
352
|
-
def patch(self, kind: str, name: str, namespace: str, conf: dict):
|
|
353
|
-
try:
|
|
354
|
-
logger.info(f"Patching {kind} for `{name}` with namespace `{namespace}`")
|
|
355
|
-
self.kubernetes.patch(kind, name, namespace, conf)
|
|
356
|
-
except ApiException as e:
|
|
357
|
-
logger.error(
|
|
358
|
-
f"Error patching {kind} for `{name}` with namespace `{namespace}` -- {e}"
|
|
359
|
-
)
|
|
360
|
-
|
|
361
|
-
def read(self, kind: str, name: str, namespace: str):
|
|
362
|
-
try:
|
|
363
|
-
logger.info(f"Retrieving {kind} for `{name}` with namespace `{namespace}`")
|
|
364
|
-
return self.kubernetes.read(kind, name, namespace=namespace)
|
|
365
|
-
except ApiException as e:
|
|
366
|
-
logger.error(
|
|
367
|
-
f"Error retrieving {kind} for `{name}` with namespace `{namespace}` -- {e}"
|
|
368
|
-
)
|
|
369
|
-
return e
|
|
370
|
-
|
|
371
|
-
def delete(self, kind: str, name: str, namespace: str):
|
|
372
|
-
try:
|
|
373
|
-
logger.info(f"Deleting {kind} for `{name}` with namespace `{namespace}`")
|
|
374
|
-
return self.kubernetes.delete(kind, name, namespace=namespace)
|
|
375
|
-
except ApiException as e:
|
|
376
|
-
logger.error(
|
|
377
|
-
f"Error deleting {kind} for `{name}` with namespace `{namespace}` -- {e}"
|
|
378
|
-
)
|
|
379
|
-
return e
|
|
380
|
-
|
|
381
|
-
def check(self, namespace, svc_name, hook):
|
|
382
|
-
svc = self.meta.get_service(svc_name, hook)
|
|
383
|
-
|
|
384
|
-
if not svc.is_running():
|
|
385
|
-
if svc.manifest:
|
|
386
|
-
config_map = svc.manifest
|
|
387
|
-
pod_name = ""
|
|
388
|
-
old_config_map = deepcopy(svc.manifest_meta.get("__OLD_CONFIG__", {}))
|
|
389
|
-
unsafe_paraphrase = svc.manifest_meta.get("__UNSAFE_PARAPHRASE__", "")
|
|
390
|
-
for kind, confs in config_map.items():
|
|
391
|
-
for conf in confs:
|
|
392
|
-
name = conf["metadata"]["name"]
|
|
393
|
-
names = old_config_map.get(kind, [])
|
|
394
|
-
if name in names:
|
|
395
|
-
names.remove(name)
|
|
396
|
-
|
|
397
|
-
if kind == "Service":
|
|
398
|
-
pod_name = name
|
|
399
|
-
res = self.read(kind, name, namespace)
|
|
400
|
-
if hasattr(res, "status") and res.status == 404 and conf:
|
|
401
|
-
self.create(kind, name, namespace, conf)
|
|
402
|
-
elif not isinstance(res, ApiException) and res.metadata:
|
|
403
|
-
if res.metadata.labels:
|
|
404
|
-
config_version = res.metadata.labels.get(
|
|
405
|
-
"config_version", 1
|
|
406
|
-
)
|
|
407
|
-
else:
|
|
408
|
-
config_version = 1
|
|
409
|
-
|
|
410
|
-
if config_version != conf.get("metadata").get(
|
|
411
|
-
"labels", {}
|
|
412
|
-
).get("config_version", 1):
|
|
413
|
-
self.patch(kind, name, namespace, conf)
|
|
414
|
-
|
|
415
|
-
if (
|
|
416
|
-
old_config_map
|
|
417
|
-
and type(old_config_map) is dict
|
|
418
|
-
and kind in old_config_map
|
|
419
|
-
and name in old_config_map[kind]
|
|
420
|
-
):
|
|
421
|
-
old_config_map.get(kind, []).remove(name)
|
|
422
|
-
|
|
423
|
-
for to_be_removed in old_config_map.get(kind, []):
|
|
424
|
-
res = self.read(kind, to_be_removed, namespace)
|
|
425
|
-
if not isinstance(res, ApiException) and res.metadata:
|
|
426
|
-
if (
|
|
427
|
-
kind not in UNSAFE_KINDS
|
|
428
|
-
or unsafe_paraphrase == UNSAFE_PARAPHRASE
|
|
429
|
-
):
|
|
430
|
-
self.delete(kind, to_be_removed, namespace)
|
|
431
|
-
else:
|
|
432
|
-
logger.info(
|
|
433
|
-
f"You don't have permission to delete `{kind}` for `{to_be_removed}` with namespace `{namespace}`!"
|
|
434
|
-
)
|
|
435
|
-
|
|
436
|
-
if self.kubernetes.is_running(pod_name, namespace):
|
|
437
|
-
logger.info(
|
|
438
|
-
f"Pod state is running. Trying to Restart {svc_name}..."
|
|
439
|
-
)
|
|
440
|
-
svc.reset(hook)
|
|
441
|
-
else:
|
|
442
|
-
logger.info(f"Restarting {svc_name}...")
|
|
443
|
-
svc.reset(hook)
|
|
444
|
-
|
|
445
|
-
###################################################
|
|
446
|
-
# SERVICE HANDLER #
|
|
447
|
-
###################################################
|
|
448
|
-
|
|
449
|
-
def add_service_builder(self, name, svc):
|
|
450
|
-
if self.services.get(name):
|
|
451
|
-
raise Exception(f"{name} already exists!")
|
|
452
|
-
|
|
453
|
-
self.services[name] = svc
|
|
454
|
-
|
|
455
|
-
def build_service(self, name, background, *args, **kwargs):
|
|
456
|
-
svc = self.services.get(name)
|
|
457
|
-
|
|
458
|
-
if not svc:
|
|
459
|
-
logger.error(f"Service {name} is not yet set!")
|
|
460
|
-
return None
|
|
461
|
-
|
|
462
|
-
svc = svc(*args, **kwargs)
|
|
463
|
-
|
|
464
|
-
if background:
|
|
465
|
-
self.background[name] = svc
|
|
466
|
-
|
|
467
|
-
return svc
|
|
468
|
-
|
|
469
|
-
def get_service(self, name, *args, **kwargs):
|
|
470
|
-
svc = self.background.get(name)
|
|
471
|
-
|
|
472
|
-
if not svc:
|
|
473
|
-
return self.build_service(name, True, *args, **kwargs)
|
|
474
|
-
|
|
475
|
-
return svc
|
|
476
|
-
|
|
477
|
-
###################################################
|
|
478
|
-
# CONTEXT HANDLER #
|
|
479
|
-
###################################################
|
|
480
|
-
|
|
481
|
-
def add_context(self, name, cls, *args, **kwargs):
|
|
482
|
-
self.context[name] = {"class": cls, "args": args, "kwargs": kwargs}
|
|
483
|
-
|
|
484
|
-
def build_context(self, ctx, *args, **kwargs):
|
|
485
|
-
ctx = self.context[ctx]
|
|
486
|
-
return ctx["class"](*args, *ctx["args"], **kwargs, **ctx["kwargs"])
|
|
487
|
-
|
|
488
|
-
###################################################
|
|
489
|
-
# ACTION MANAGER #
|
|
490
|
-
###################################################
|
|
491
|
-
def load_action_config(self, config, name):
|
|
492
|
-
"""
|
|
493
|
-
Load the config for an action
|
|
494
|
-
"""
|
|
495
|
-
return load_action_config(config, name)
|
|
496
|
-
|
|
497
|
-
def load_actions(self, name, mode):
|
|
498
|
-
"""
|
|
499
|
-
Load an action as local, module or remote.
|
|
500
|
-
"""
|
|
501
|
-
# Using module for local
|
|
502
|
-
mode = "module" if mode == "local" else mode
|
|
503
|
-
|
|
504
|
-
if mode == "module":
|
|
505
|
-
self.actions_optimizer.load_action_module(name)
|
|
506
|
-
elif mode == "remote":
|
|
507
|
-
self.actions_optimizer.load_action_remote(name)
|
|
508
|
-
|
|
509
|
-
def unload_actions(self, name, mode, retire_svc):
|
|
510
|
-
"""
|
|
511
|
-
Unload an action
|
|
512
|
-
"""
|
|
513
|
-
# We are using module for local
|
|
514
|
-
mode = "module" if mode == "local" else mode
|
|
515
|
-
if mode == "auto":
|
|
516
|
-
res = self.actions_optimizer.unload_action_auto(name)
|
|
517
|
-
if not res[0]:
|
|
518
|
-
return res
|
|
519
|
-
if retire_svc:
|
|
520
|
-
self.retire_uservice(name)
|
|
521
|
-
return res
|
|
522
|
-
elif mode == "module":
|
|
523
|
-
return self.actions_optimizer.unload_action_module(name)
|
|
524
|
-
elif mode == "remote":
|
|
525
|
-
res = self.actions_optimizer.unload_action_remote(name)
|
|
526
|
-
if not res[0]:
|
|
527
|
-
return res
|
|
528
|
-
if retire_svc:
|
|
529
|
-
self.retire_uservice(name)
|
|
530
|
-
return res
|
|
531
|
-
else:
|
|
532
|
-
return (False, f"Unrecognized action mode {mode}.")
|
|
533
|
-
|
|
534
|
-
def retire_uservice(self, name):
|
|
535
|
-
"""
|
|
536
|
-
Retire a remote microservice for the action.
|
|
537
|
-
"""
|
|
538
|
-
self.actions_optimizer.retire_remote(name)
|
|
539
|
-
|
|
540
|
-
def get_actions_status(self, name=""):
|
|
541
|
-
"""
|
|
542
|
-
Return the status of the action
|
|
543
|
-
"""
|
|
544
|
-
return self.actions_optimizer.get_actions_status(name)
|
|
545
|
-
|
|
546
|
-
def actions_tracking_start(self):
|
|
547
|
-
""" """
|
|
548
|
-
self.actions_history["active"] = True
|
|
549
|
-
self.actions_history["history"] = [{"ts": time.time()}]
|
|
550
|
-
self.actions_calls.clear()
|
|
551
|
-
|
|
552
|
-
def actions_tracking_stop(self):
|
|
553
|
-
""" """
|
|
554
|
-
if not self.actions_history["active"]:
|
|
555
|
-
return []
|
|
556
|
-
|
|
557
|
-
self.actions_optimizer.summarize_action_calls()
|
|
558
|
-
|
|
559
|
-
return self.actions_history["history"]
|
|
560
|
-
|
|
561
|
-
def benchmark_start(self):
|
|
562
|
-
"""
|
|
563
|
-
Put JSORC under benchmark mode.
|
|
564
|
-
"""
|
|
565
|
-
self.benchmark["jsorc"]["active"] = True
|
|
566
|
-
self.benchmark["jsorc"]["requests"] = {}
|
|
567
|
-
self.benchmark["jsorc"]["start_ts"] = time.time()
|
|
568
|
-
|
|
569
|
-
def state_tracking_start(self):
|
|
570
|
-
"""
|
|
571
|
-
Ask JSORC to start tracking the state of the system as observed by JSORC on every interval.
|
|
572
|
-
"""
|
|
573
|
-
self.system_states = {"active": True, "states": []}
|
|
574
|
-
|
|
575
|
-
def state_tracking_stop(self):
|
|
576
|
-
"""
|
|
577
|
-
Stop state tracking for JSORC
|
|
578
|
-
"""
|
|
579
|
-
ret = self.system_states
|
|
580
|
-
self.system_states = {"active": True, "states": []}
|
|
581
|
-
return ret
|
|
582
|
-
|
|
583
|
-
def state_tracking_report(self):
|
|
584
|
-
"""
|
|
585
|
-
Return state tracking history so far
|
|
586
|
-
"""
|
|
587
|
-
return self.system_states
|
|
588
|
-
|
|
589
|
-
def record_system_state(self):
|
|
590
|
-
"""
|
|
591
|
-
Record system state
|
|
592
|
-
"""
|
|
593
|
-
if self.system_states["active"]:
|
|
594
|
-
ts = int(time.time())
|
|
595
|
-
prom_profile = self.prometheus.info(
|
|
596
|
-
namespace=self.namespace,
|
|
597
|
-
exclude_prom=True,
|
|
598
|
-
timestamp=ts,
|
|
599
|
-
duration=self.backoff_interval,
|
|
600
|
-
)
|
|
601
|
-
self.system_states["states"].append(
|
|
602
|
-
{
|
|
603
|
-
"ts": ts,
|
|
604
|
-
"actions": self.get_actions_status(name=""),
|
|
605
|
-
"prometheus": prom_profile,
|
|
606
|
-
}
|
|
607
|
-
)
|
|
608
|
-
|
|
609
|
-
def benchmark_stop(self, report):
|
|
610
|
-
"""
|
|
611
|
-
Stop benchmark mode and report result during the benchmark period
|
|
612
|
-
"""
|
|
613
|
-
if not self.benchmark["jsorc"]["active"]:
|
|
614
|
-
return {}
|
|
615
|
-
|
|
616
|
-
res = self.benchmark_report()
|
|
617
|
-
self.benchmark["jsorc"]["requests"] = {}
|
|
618
|
-
self.benchmark["jsorc"]["active"] = False
|
|
619
|
-
|
|
620
|
-
if report:
|
|
621
|
-
return res
|
|
622
|
-
else:
|
|
623
|
-
return {}
|
|
624
|
-
|
|
625
|
-
def benchmark_report(self):
|
|
626
|
-
"""
|
|
627
|
-
Summarize benchmark results and report.
|
|
628
|
-
"""
|
|
629
|
-
summary = {}
|
|
630
|
-
duration = time.time() - self.benchmark["jsorc"]["start_ts"]
|
|
631
|
-
for request, data in self.benchmark["jsorc"]["requests"].items():
|
|
632
|
-
summary[request] = {}
|
|
633
|
-
all_reqs = []
|
|
634
|
-
for req_name, times in data.items():
|
|
635
|
-
if len(times) == 0:
|
|
636
|
-
continue
|
|
637
|
-
all_reqs.extend(times)
|
|
638
|
-
summary[request][req_name] = {
|
|
639
|
-
"throughput": len(times) / duration,
|
|
640
|
-
"average_latency": sum(times) / len(times) * 1000,
|
|
641
|
-
"50th_latency": np.percentile(times, 50) * 1000,
|
|
642
|
-
"90th_latency": np.percentile(times, 90) * 1000,
|
|
643
|
-
"95th_latency": np.percentile(times, 95) * 1000,
|
|
644
|
-
"99th_latency": np.percentile(times, 99) * 1000,
|
|
645
|
-
}
|
|
646
|
-
summary[request]["all"] = {
|
|
647
|
-
"throughput": len(all_reqs) / duration,
|
|
648
|
-
"average_latency": sum(all_reqs) / len(all_reqs) * 1000,
|
|
649
|
-
"50th_latency": np.percentile(all_reqs, 50) * 1000,
|
|
650
|
-
"90th_latency": np.percentile(all_reqs, 90) * 1000,
|
|
651
|
-
"95th_latency": np.percentile(all_reqs, 95) * 1000,
|
|
652
|
-
"99th_latency": np.percentile(all_reqs, 99) * 1000,
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
return summary
|
|
656
|
-
|
|
657
|
-
def record_state(self):
|
|
658
|
-
"""
|
|
659
|
-
Record the current state of the system observed by JSORC
|
|
660
|
-
"""
|
|
661
|
-
|
|
662
|
-
def add_to_benchmark(self, request_type, request, request_time):
|
|
663
|
-
"""
|
|
664
|
-
Add requests to benchmark performance tracking
|
|
665
|
-
"""
|
|
666
|
-
for bm in self.benchmark.values():
|
|
667
|
-
if request_type not in bm["requests"]:
|
|
668
|
-
bm["requests"][request_type] = {"_default_": []}
|
|
669
|
-
if request_type == "walker_run":
|
|
670
|
-
walker_name = dict(request.data)["name"]
|
|
671
|
-
if walker_name not in bm["requests"][request_type]:
|
|
672
|
-
bm["requests"][request_type][walker_name] = []
|
|
673
|
-
bm["requests"][request_type][walker_name].append(request_time)
|
|
674
|
-
else:
|
|
675
|
-
bm["requests"][request_type]["_default_"].append(request_time)
|
|
676
|
-
|
|
677
|
-
def set_action_policy(self, policy_name, policy_params):
|
|
678
|
-
"""
|
|
679
|
-
Set an action optimizer policy
|
|
680
|
-
"""
|
|
681
|
-
return self.actions_optimizer.set_action_policy(policy_name, policy_params)
|
|
682
|
-
|
|
683
|
-
def get_action_policy(self):
|
|
684
|
-
"""
|
|
685
|
-
Get the current action optimization policy
|
|
686
|
-
"""
|
|
687
|
-
return self.actions_optimizer.get_action_policy()
|
|
688
|
-
|
|
689
|
-
def pre_action_call_hook(self, *args):
|
|
690
|
-
pass
|
|
691
|
-
|
|
692
|
-
def post_action_call_hook(self, *args):
|
|
693
|
-
action_name = args[0]
|
|
694
|
-
action_time = args[1]
|
|
695
|
-
if action_name not in self.actions_calls:
|
|
696
|
-
self.actions_calls[action_name] = []
|
|
697
|
-
|
|
698
|
-
self.actions_calls[action_name].append(action_time)
|
|
699
|
-
|
|
700
|
-
def pre_request_hook(self, *args):
|
|
701
|
-
pass
|
|
702
|
-
|
|
703
|
-
def post_request_hook(self, *args):
|
|
704
|
-
request_type = args[0]
|
|
705
|
-
request = args[1]
|
|
706
|
-
request_time = args[2]
|
|
707
|
-
if self.benchmark["jsorc"]["active"]:
|
|
708
|
-
self.add_to_benchmark(request_type, request, request_time)
|
|
709
|
-
|
|
710
|
-
def optimize(self, jsorc_interval):
|
|
711
|
-
self.actions_optimizer.run(jsorc_interval)
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
class MetaProperties:
|
|
715
|
-
def __init__(self, cls):
|
|
716
|
-
self.cls = cls
|
|
717
|
-
|
|
718
|
-
if not hasattr(cls, "_app"):
|
|
719
|
-
setattr(cls, "_app", None)
|
|
720
|
-
setattr(cls, "_enabled", True)
|
|
721
|
-
setattr(cls, "_state", Ss.NOT_STARTED)
|
|
722
|
-
setattr(cls, "_quiet", False)
|
|
723
|
-
setattr(cls, "_running_interval", 0)
|
|
724
|
-
|
|
725
|
-
@property
|
|
726
|
-
def app(self) -> JsOrc:
|
|
727
|
-
return self.cls._app
|
|
728
|
-
|
|
729
|
-
@app.setter
|
|
730
|
-
def app(self, val: JsOrc):
|
|
731
|
-
self.cls._app = val
|
|
732
|
-
|
|
733
|
-
@property
|
|
734
|
-
def state(self) -> Ss:
|
|
735
|
-
return self.cls._state
|
|
736
|
-
|
|
737
|
-
@state.setter
|
|
738
|
-
def state(self, val: Ss):
|
|
739
|
-
self.cls._state = val
|
|
740
|
-
|
|
741
|
-
@property
|
|
742
|
-
def enabled(self) -> bool:
|
|
743
|
-
return self.cls._enabled
|
|
744
|
-
|
|
745
|
-
@enabled.setter
|
|
746
|
-
def enabled(self, val: bool):
|
|
747
|
-
pass
|
|
748
|
-
|
|
749
|
-
@property
|
|
750
|
-
def quiet(self) -> bool:
|
|
751
|
-
return self.cls._quiet
|
|
752
|
-
|
|
753
|
-
@quiet.setter
|
|
754
|
-
def quiet(self, val: bool):
|
|
755
|
-
pass
|
|
756
|
-
|
|
757
|
-
@property
|
|
758
|
-
def running_interval(self) -> int:
|
|
759
|
-
return self.cls._running_interval
|
|
760
|
-
|
|
761
|
-
@running_interval.setter
|
|
762
|
-
def running_interval(self, val: int):
|
|
763
|
-
self.cls._running_interval = val
|