appmesh 0.2.3__py3-none-any.whl → 0.2.4__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.
- appmesh/appmesh_client.py +209 -42
- {appmesh-0.2.3.dist-info → appmesh-0.2.4.dist-info}/METADATA +1 -1
- appmesh-0.2.4.dist-info/RECORD +6 -0
- appmesh-0.2.3.dist-info/RECORD +0 -6
- {appmesh-0.2.3.dist-info → appmesh-0.2.4.dist-info}/WHEEL +0 -0
- {appmesh-0.2.3.dist-info → appmesh-0.2.4.dist-info}/top_level.txt +0 -0
appmesh/appmesh_client.py
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
import abc
|
4
4
|
import aniso8601
|
5
5
|
import base64
|
6
|
+
import copy
|
6
7
|
import json
|
7
8
|
import os
|
8
9
|
import socket
|
@@ -25,6 +26,165 @@ TCP_MESSAGE_HEADER_LENGTH = 4
|
|
25
26
|
_SSL_CA_PEM_FILE = "/opt/appmesh/ssl/ca.pem"
|
26
27
|
|
27
28
|
|
29
|
+
def _get_str_item(data, key):
|
30
|
+
return str(data[key]) if (data and key in data and data[key]) else None
|
31
|
+
|
32
|
+
|
33
|
+
def _get_int_item(data, key):
|
34
|
+
return int(data[key]) if (data and key in data and data[key]) else None
|
35
|
+
|
36
|
+
|
37
|
+
def _get_bool_item(data, key):
|
38
|
+
return bool(data[key]) if (data and key in data and data[key]) else None
|
39
|
+
|
40
|
+
|
41
|
+
def _get_dict_item(data, key):
|
42
|
+
return data[key] if (data and key in data and data[key]) else None
|
43
|
+
|
44
|
+
|
45
|
+
class App(object):
|
46
|
+
"""
|
47
|
+
App object present an application in App Mesh
|
48
|
+
"""
|
49
|
+
|
50
|
+
class Behavior(object):
|
51
|
+
"""
|
52
|
+
Application error handling behavior definition object
|
53
|
+
"""
|
54
|
+
|
55
|
+
def __init__(self, data=None) -> None:
|
56
|
+
if isinstance(data, (str, bytes, bytearray)):
|
57
|
+
data = json.loads(data)
|
58
|
+
self.exit = _get_str_item(data, "exit")
|
59
|
+
self.control = copy.deepcopy(data)
|
60
|
+
|
61
|
+
class DailyLimitation(object):
|
62
|
+
"""
|
63
|
+
Application avialable day time definition object
|
64
|
+
"""
|
65
|
+
|
66
|
+
def __init__(self, data=None) -> None:
|
67
|
+
if isinstance(data, (str, bytes, bytearray)):
|
68
|
+
data = json.loads(data)
|
69
|
+
self.daily_start = _get_int_item(data, "daily_start")
|
70
|
+
self.daily_end = _get_int_item(data, "daily_end")
|
71
|
+
|
72
|
+
class ResourceLimitation(object):
|
73
|
+
"""
|
74
|
+
Application cgroup limitation definition object
|
75
|
+
"""
|
76
|
+
|
77
|
+
def __init__(self, data=None) -> None:
|
78
|
+
if isinstance(data, (str, bytes, bytearray)):
|
79
|
+
data = json.loads(data)
|
80
|
+
self.cpu_shares = _get_int_item(data, "cpu_shares")
|
81
|
+
self.memory_mb = _get_int_item(data, "memory_mb")
|
82
|
+
self.memory_virt_mb = _get_int_item(data, "memory_virt_mb")
|
83
|
+
|
84
|
+
def __init__(self, data=None):
|
85
|
+
"""Construct an App Mesh Application object
|
86
|
+
|
87
|
+
Args:
|
88
|
+
data (str | dict | json, optional): application definition data
|
89
|
+
"""
|
90
|
+
|
91
|
+
if isinstance(data, (str, bytes, bytearray)):
|
92
|
+
data = json.loads(data)
|
93
|
+
|
94
|
+
# mandatory parameters
|
95
|
+
self.name = _get_str_item(data, "name")
|
96
|
+
self.command = _get_str_item(data, "command")
|
97
|
+
|
98
|
+
self.shell = _get_bool_item(data, "shell")
|
99
|
+
self.description = _get_str_item(data, "description")
|
100
|
+
self.metadata = _get_str_item(data, "metadata")
|
101
|
+
self.working_dir = _get_str_item(data, "working_dir")
|
102
|
+
self.status = _get_int_item(data, "status")
|
103
|
+
self.docker_image = _get_str_item(data, "docker_image")
|
104
|
+
self.stdout_cache_num = _get_int_item(data, "stdout_cache_num")
|
105
|
+
|
106
|
+
self.start_time = _get_int_item(data, "start_time")
|
107
|
+
self.end_time = _get_int_item(data, "end_time")
|
108
|
+
self.interval = _get_int_item(data, "interval")
|
109
|
+
self.cron = _get_bool_item(data, "cron")
|
110
|
+
self.daily_limitation = App.DailyLimitation(_get_dict_item(data, "daily_limitation"))
|
111
|
+
|
112
|
+
self.retention = _get_str_item(data, "retention")
|
113
|
+
self.extra_time = _get_str_item(data, "extra_time")
|
114
|
+
|
115
|
+
self.health_check_cmd = _get_str_item(data, "health_check_cmd")
|
116
|
+
self.permission = _get_int_item(data, "permission")
|
117
|
+
self.behavior = App.Behavior(_get_dict_item(data, "behavior"))
|
118
|
+
|
119
|
+
self.env = dict()
|
120
|
+
if data and "env" in data:
|
121
|
+
for k, v in data["env"].items():
|
122
|
+
self.env[k] = v
|
123
|
+
self.sec_env = dict()
|
124
|
+
if data and "sec_env" in data:
|
125
|
+
for k, v in data["sec_env"].items():
|
126
|
+
self.sec_env[k] = v
|
127
|
+
self.pid = _get_int_item(data, "pid")
|
128
|
+
self.resource_limit = App.ResourceLimitation(_get_dict_item(data, "resource_limit"))
|
129
|
+
|
130
|
+
# readonly attributes
|
131
|
+
self.owner = _get_str_item(data, "owner")
|
132
|
+
self.pstree = _get_str_item(data, "pstree")
|
133
|
+
self.container_id = _get_str_item(data, "container_id")
|
134
|
+
self.memory = _get_int_item(data, "memory")
|
135
|
+
self.cpu = _get_int_item(data, "cpu")
|
136
|
+
self.fd = _get_int_item(data, "fd")
|
137
|
+
self.last_start_time = _get_int_item(data, "last_start_time")
|
138
|
+
self.last_exit_time = _get_int_item(data, "last_exit_time")
|
139
|
+
self.health = _get_int_item(data, "health")
|
140
|
+
self.version = _get_int_item(data, "version")
|
141
|
+
self.return_code = _get_int_item(data, "return_code")
|
142
|
+
|
143
|
+
def __str__(self) -> str:
|
144
|
+
output = copy.deepcopy(self.__dict__)
|
145
|
+
output["behavior"] = copy.deepcopy(self.behavior.__dict__)
|
146
|
+
output["daily_limitation"] = copy.deepcopy(self.daily_limitation.__dict__)
|
147
|
+
output["resource_limit"] = copy.deepcopy(self.resource_limit.__dict__)
|
148
|
+
|
149
|
+
def clean_empty_item(data, key) -> None:
|
150
|
+
value = data[key]
|
151
|
+
if not value:
|
152
|
+
del data[key]
|
153
|
+
elif isinstance(value, dict):
|
154
|
+
for k in list(value):
|
155
|
+
clean_empty_item(value, k)
|
156
|
+
|
157
|
+
for k in list(output):
|
158
|
+
clean_empty_item(output, k)
|
159
|
+
for k in list(output):
|
160
|
+
clean_empty_item(output, k)
|
161
|
+
return json.dumps(output, indent=2)
|
162
|
+
|
163
|
+
|
164
|
+
class Run(object):
|
165
|
+
"""
|
166
|
+
Application run object indicate to a remote run from run_async()
|
167
|
+
"""
|
168
|
+
|
169
|
+
def __init__(self, client, app_name: str, process_id: str):
|
170
|
+
self.app_name = app_name
|
171
|
+
self.proc_uid = process_id
|
172
|
+
self.__client = client
|
173
|
+
|
174
|
+
def wait(self, stdout_print: bool = True, timeout: int = 0) -> int:
|
175
|
+
"""Wait for an async run to be finished
|
176
|
+
|
177
|
+
Args:
|
178
|
+
run (Run): asyncrized run result from run_async().
|
179
|
+
stdout_print (bool, optional): print remote stdout to local or not.
|
180
|
+
timeout (int, optional): wait max timeout seconds and return if not finished, 0 means wait until finished
|
181
|
+
|
182
|
+
Returns:
|
183
|
+
int: return exit code if process finished, return None for timeout or exception.
|
184
|
+
"""
|
185
|
+
return self.__client.run_async_wait(self, stdout_print, timeout)
|
186
|
+
|
187
|
+
|
28
188
|
class AppMeshClient(metaclass=abc.ABCMeta):
|
29
189
|
"""
|
30
190
|
Client object used to access App Mesh REST Service
|
@@ -132,7 +292,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
132
292
|
if self.jwt_auth_enable:
|
133
293
|
self.jwt_token = token
|
134
294
|
headers = {}
|
135
|
-
if permission
|
295
|
+
if permission:
|
136
296
|
headers["Auth-Permission"] = permission
|
137
297
|
resp = self._request_http(AppMeshClient.Method.POST, path="/appmesh/auth", header=headers)
|
138
298
|
if resp.status_code == HTTPStatus.OK:
|
@@ -146,28 +306,39 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
146
306
|
########################################
|
147
307
|
# Application view
|
148
308
|
########################################
|
149
|
-
def app_view(self, app_name: str):
|
150
|
-
"""Get application information
|
309
|
+
def app_view(self, app_name: str) -> App:
|
310
|
+
"""Get one application information
|
151
311
|
|
152
312
|
Args:
|
153
313
|
app_name (str): the application name.
|
154
314
|
|
155
315
|
Returns:
|
156
|
-
|
157
|
-
|
316
|
+
App: the application object both contain static configuration and runtime information.
|
317
|
+
|
318
|
+
Exception:
|
319
|
+
failed request or no such application
|
158
320
|
"""
|
159
321
|
resp = self._request_http(AppMeshClient.Method.GET, path=f"/appmesh/app/{app_name}")
|
160
|
-
|
322
|
+
if resp.status_code != HTTPStatus.OK:
|
323
|
+
raise Exception(resp.text)
|
324
|
+
return App(resp.json())
|
161
325
|
|
162
326
|
def app_view_all(self):
|
163
|
-
"""Get all applications
|
327
|
+
"""Get all applications
|
164
328
|
|
165
329
|
Returns:
|
166
|
-
|
167
|
-
|
330
|
+
list: the application object both contain static configuration and runtime information, only return applications that the user has permissions.
|
331
|
+
|
332
|
+
Exception:
|
333
|
+
failed request or no such application
|
168
334
|
"""
|
169
335
|
resp = self._request_http(AppMeshClient.Method.GET, path="/appmesh/applications")
|
170
|
-
|
336
|
+
if resp.status_code != HTTPStatus.OK:
|
337
|
+
raise Exception(resp.text)
|
338
|
+
apps = []
|
339
|
+
for app in resp.json():
|
340
|
+
apps.append(App(app))
|
341
|
+
return apps
|
171
342
|
|
172
343
|
def app_output(self, app_name: str, stdout_position: int = 0, stdout_index: int = 0, stdout_maxsize: int = 10240, process_uuid: str = "", timeout: int = 0):
|
173
344
|
"""Get application stdout/stderr
|
@@ -217,18 +388,22 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
217
388
|
########################################
|
218
389
|
# Application manage
|
219
390
|
########################################
|
220
|
-
def app_add(self,
|
391
|
+
def app_add(self, app: App) -> App:
|
221
392
|
"""Register an application
|
222
393
|
|
223
394
|
Args:
|
224
|
-
|
395
|
+
app (App): the application definition.
|
225
396
|
|
226
397
|
Returns:
|
227
|
-
|
228
|
-
|
398
|
+
App: resigtered application object.
|
399
|
+
|
400
|
+
Exception:
|
401
|
+
failed request
|
229
402
|
"""
|
230
|
-
resp = self._request_http(AppMeshClient.Method.PUT, path="/appmesh/app/{0}".format(
|
231
|
-
|
403
|
+
resp = self._request_http(AppMeshClient.Method.PUT, path="/appmesh/app/{0}".format(app.name), body=str(app))
|
404
|
+
if resp.status_code != HTTPStatus.OK:
|
405
|
+
raise Exception(resp.text)
|
406
|
+
return App(resp.json())
|
232
407
|
|
233
408
|
def app_delete(self, app_name: str):
|
234
409
|
"""Remove an application.
|
@@ -762,7 +937,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
762
937
|
|
763
938
|
def run_async(
|
764
939
|
self,
|
765
|
-
|
940
|
+
app: App,
|
766
941
|
max_time_seconds=DEFAULT_RUN_APP_TIMEOUT_SECONDS,
|
767
942
|
life_cycle_seconds=DEFAULT_RUN_APP_LIFECYCLE_SECONDS,
|
768
943
|
):
|
@@ -770,7 +945,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
770
945
|
Asyncrized run will not block process
|
771
946
|
|
772
947
|
Args:
|
773
|
-
|
948
|
+
app (App): application object.
|
774
949
|
max_time_seconds (int | str, optional): max run time for the remote process, support ISO 8601 durations (e.g., 'P1Y2M3DT4H5M6S' 'P5W').
|
775
950
|
life_cycle_seconds (int | str, optional): max lifecycle time for the remote process. support ISO 8601 durations (e.g., 'P1Y2M3DT4H5M6S' 'P5W').
|
776
951
|
|
@@ -781,25 +956,19 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
781
956
|
path = "/appmesh/app/run"
|
782
957
|
resp = self._request_http(
|
783
958
|
AppMeshClient.Method.POST,
|
784
|
-
body=
|
959
|
+
body=str(app),
|
785
960
|
path=path,
|
786
961
|
query={"timeout": self._parse_duration(max_time_seconds), "lifecycle": self._parse_duration(life_cycle_seconds)},
|
787
962
|
)
|
788
|
-
if resp.status_code
|
789
|
-
|
790
|
-
|
791
|
-
return (app_name, process_uuid)
|
792
|
-
else:
|
793
|
-
print(resp.text)
|
794
|
-
return None
|
963
|
+
if resp.status_code != HTTPStatus.OK:
|
964
|
+
raise Exception(resp.text)
|
965
|
+
return Run(self, resp.json()["name"], resp.json()["process_uuid"])
|
795
966
|
|
796
|
-
def run_async_wait(self,
|
967
|
+
def run_async_wait(self, run: Run, stdout_print: bool = True, timeout: int = 0) -> int:
|
797
968
|
"""Wait for an async run to be finished
|
798
969
|
|
799
970
|
Args:
|
800
|
-
|
801
|
-
async_tuple[0] app_name: application name from run_async
|
802
|
-
async_tuple[1] process_uuid: process uuid
|
971
|
+
run (Run): asyncrized run result from run_async().
|
803
972
|
stdout_print (bool, optional): print remote stdout to local or not.
|
804
973
|
timeout (int, optional): wait max timeout seconds and return if not finished, 0 means wait until finished
|
805
974
|
|
@@ -807,23 +976,21 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
807
976
|
int: return exit code if process finished, return None for timeout or exception.
|
808
977
|
"""
|
809
978
|
exit_code = None
|
810
|
-
if
|
811
|
-
app_name = async_tuple[0]
|
812
|
-
process_uuid = async_tuple[1]
|
979
|
+
if run:
|
813
980
|
output_position = 0
|
814
981
|
start = datetime.now()
|
815
982
|
interval = 1 if self.__class__.__name__ == "AppMeshClient" else 1000
|
816
|
-
while len(
|
983
|
+
while len(run.proc_uid) > 0:
|
817
984
|
success, output, position, exit_code = self.app_output(
|
818
|
-
app_name=app_name, stdout_position=output_position, stdout_index=0, process_uuid=
|
985
|
+
app_name=run.app_name, stdout_position=output_position, stdout_index=0, process_uuid=run.proc_uid, timeout=interval
|
819
986
|
)
|
820
|
-
if output
|
987
|
+
if output and stdout_print:
|
821
988
|
print(output, end="")
|
822
989
|
if position is not None:
|
823
990
|
output_position = position
|
824
991
|
if exit_code is not None:
|
825
992
|
# success
|
826
|
-
self.app_delete(app_name)
|
993
|
+
self.app_delete(run.app_name)
|
827
994
|
break
|
828
995
|
if not success:
|
829
996
|
# failed
|
@@ -835,7 +1002,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
835
1002
|
|
836
1003
|
def run_sync(
|
837
1004
|
self,
|
838
|
-
|
1005
|
+
app: App,
|
839
1006
|
stdout_print: bool = True,
|
840
1007
|
max_time_seconds=DEFAULT_RUN_APP_TIMEOUT_SECONDS,
|
841
1008
|
life_cycle_seconds=DEFAULT_RUN_APP_LIFECYCLE_SECONDS,
|
@@ -844,7 +1011,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
844
1011
|
The synchronized run will block the process until the remote run is finished then return the result from HTTP response
|
845
1012
|
|
846
1013
|
Args:
|
847
|
-
|
1014
|
+
app (App): application object.
|
848
1015
|
stdout_print (bool, optional): whether print remote stdout to local or not. Defaults to True.
|
849
1016
|
max_time_seconds (int | str, optional): max run time for the remote process. support ISO 8601 durations (e.g., 'P1Y2M3DT4H5M6S' 'P5W').
|
850
1017
|
life_cycle_seconds (int | str, optional): max lifecycle time for the remote process. support ISO 8601 durations (e.g., 'P1Y2M3DT4H5M6S' 'P5W').
|
@@ -855,7 +1022,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
855
1022
|
path = "/appmesh/app/syncrun"
|
856
1023
|
resp = self._request_http(
|
857
1024
|
AppMeshClient.Method.POST,
|
858
|
-
body=
|
1025
|
+
body=str(app),
|
859
1026
|
path=path,
|
860
1027
|
query={"timeout": self._parse_duration(max_time_seconds), "lifecycle": self._parse_duration(life_cycle_seconds)},
|
861
1028
|
)
|
@@ -883,7 +1050,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
883
1050
|
requests.Response: HTTP response
|
884
1051
|
"""
|
885
1052
|
rest_url = parse.urljoin(self.server_url, path)
|
886
|
-
if self.jwt_token
|
1053
|
+
if self.jwt_token:
|
887
1054
|
header["Authorization"] = "Bearer " + self.jwt_token
|
888
1055
|
|
889
1056
|
if method is AppMeshClient.Method.GET:
|
@@ -1018,7 +1185,7 @@ class AppMeshClientTCP(AppMeshClient):
|
|
1018
1185
|
setattr(self, k, v)
|
1019
1186
|
return self
|
1020
1187
|
|
1021
|
-
if super().jwt_token
|
1188
|
+
if super().jwt_token:
|
1022
1189
|
header["Authorization"] = "Bearer " + super().jwt_token
|
1023
1190
|
if self.__socket_client is None:
|
1024
1191
|
self.__connect_socket()
|
@@ -0,0 +1,6 @@
|
|
1
|
+
appmesh/__init__.py,sha256=xRdXeFHEieRauuJZElbEBASgXG0ZzU1a5_0isAhM7Gw,11
|
2
|
+
appmesh/appmesh_client.py,sha256=8lR_gF4_BYzWgg63XBIaJuSGJeSE7jt066v1QLFq-pc,49634
|
3
|
+
appmesh-0.2.4.dist-info/METADATA,sha256=LtKj7oblEzep3qW4RhcsWUt1oCgyUj5Nv_i-BWfE2uI,10620
|
4
|
+
appmesh-0.2.4.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
5
|
+
appmesh-0.2.4.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
|
6
|
+
appmesh-0.2.4.dist-info/RECORD,,
|
appmesh-0.2.3.dist-info/RECORD
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
appmesh/__init__.py,sha256=xRdXeFHEieRauuJZElbEBASgXG0ZzU1a5_0isAhM7Gw,11
|
2
|
-
appmesh/appmesh_client.py,sha256=wZDX62DY318vpGqmZfEcUQXYjm5heIIb5mCkTR9Q2WI,43866
|
3
|
-
appmesh-0.2.3.dist-info/METADATA,sha256=b4B2rrF9rtJSK6P-ogSIWGUEtmpew5JlfFiFsB_Z-s4,10620
|
4
|
-
appmesh-0.2.3.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
5
|
-
appmesh-0.2.3.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
|
6
|
-
appmesh-0.2.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|