appmesh 1.3.3__py3-none-any.whl → 1.3.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 +68 -68
- {appmesh-1.3.3.dist-info → appmesh-1.3.4.dist-info}/METADATA +1 -1
- appmesh-1.3.4.dist-info/RECORD +6 -0
- appmesh-1.3.3.dist-info/RECORD +0 -6
- {appmesh-1.3.3.dist-info → appmesh-1.3.4.dist-info}/WHEEL +0 -0
- {appmesh-1.3.3.dist-info → appmesh-1.3.4.dist-info}/top_level.txt +0 -0
appmesh/appmesh_client.py
CHANGED
@@ -25,14 +25,14 @@ DEFAULT_TOKEN_EXPIRE_SECONDS = "P1W" # default 7 day(s)
|
|
25
25
|
DEFAULT_RUN_APP_TIMEOUT_SECONDS = "P2D" # 2 days
|
26
26
|
DEFAULT_RUN_APP_LIFECYCLE_SECONDS = "P2DT12H" # 2.5 days
|
27
27
|
|
28
|
+
DEFAULT_SSL_CA_PEM_FILE = "/opt/appmesh/ssl/ca.pem"
|
29
|
+
DEFAULT_SSL_CLIENT_PEM_FILE = "/opt/appmesh/ssl/client.pem"
|
30
|
+
DEFAULT_SSL_CLIENT_PEM_KEY_FILE = "/opt/appmesh/ssl/client-key.pem"
|
31
|
+
|
28
32
|
REST_TEXT_MESSAGE_JSON_KEY = "message"
|
29
33
|
MESSAGE_ENCODING_UTF8 = "utf-8"
|
30
34
|
TCP_MESSAGE_HEADER_LENGTH = 4
|
31
35
|
|
32
|
-
_SSL_CA_PEM_FILE = "/opt/appmesh/ssl/ca.pem"
|
33
|
-
_SSL_CLIENT_PEM_FILE = "/opt/appmesh/ssl/client.pem"
|
34
|
-
_SSL_CLIENT_PEM_KEY_FILE = "/opt/appmesh/ssl/client-key.pem"
|
35
|
-
|
36
36
|
HTTP_USER_AGENT = "appmesh/python"
|
37
37
|
HTTP_USER_AGENT_TCP = "appmesh/python/tcp"
|
38
38
|
HTTP_HEADER_KEY_USER_AGENT = "User-Agent"
|
@@ -437,8 +437,8 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
437
437
|
def __init__(
|
438
438
|
self,
|
439
439
|
rest_url: str = "https://127.0.0.1:6060",
|
440
|
-
rest_ssl_verify=
|
441
|
-
rest_ssl_client_cert=(
|
440
|
+
rest_ssl_verify=DEFAULT_SSL_CA_PEM_FILE if os.path.exists(DEFAULT_SSL_CA_PEM_FILE) else False,
|
441
|
+
rest_ssl_client_cert=(DEFAULT_SSL_CLIENT_PEM_FILE, DEFAULT_SSL_CLIENT_PEM_KEY_FILE) if os.path.exists(DEFAULT_SSL_CLIENT_PEM_FILE) else None,
|
442
442
|
rest_timeout=(60, 300),
|
443
443
|
jwt_token=None,
|
444
444
|
):
|
@@ -1221,24 +1221,20 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1221
1221
|
########################################
|
1222
1222
|
# File management
|
1223
1223
|
########################################
|
1224
|
-
def file_download(self, file_path: str, local_file: str, apply_file_attributes: bool = True) ->
|
1224
|
+
def file_download(self, file_path: str, local_file: str, apply_file_attributes: bool = True) -> None:
|
1225
1225
|
"""Copy a remote file to local. Optionally, the local file will have the same permission as the remote file.
|
1226
1226
|
|
1227
1227
|
Args:
|
1228
1228
|
file_path (str): the remote file path.
|
1229
1229
|
local_file (str): the local file path to be downloaded.
|
1230
1230
|
apply_file_attributes (bool): whether to apply file attributes (permissions, owner, group) to the local file.
|
1231
|
-
|
1232
|
-
Returns:
|
1233
|
-
bool: success or failure.
|
1234
1231
|
"""
|
1235
1232
|
resp = self._request_http(AppMeshClient.Method.GET, path="/appmesh/file/download", header={"File-Path": file_path})
|
1236
|
-
|
1237
|
-
raise Exception(resp.text)
|
1233
|
+
resp.raise_for_status()
|
1238
1234
|
|
1239
1235
|
# Write the file content locally
|
1240
1236
|
with open(local_file, "wb") as fp:
|
1241
|
-
for chunk in resp.iter_content(chunk_size=
|
1237
|
+
for chunk in resp.iter_content(chunk_size=8192): # 8 KB
|
1242
1238
|
if chunk:
|
1243
1239
|
fp.write(chunk)
|
1244
1240
|
|
@@ -1251,11 +1247,10 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1251
1247
|
file_gid = int(resp.headers["File-Group"])
|
1252
1248
|
try:
|
1253
1249
|
os.chown(path=local_file, uid=file_uid, gid=file_gid)
|
1254
|
-
except
|
1255
|
-
print(
|
1256
|
-
return resp.status_code == HTTPStatus.OK
|
1250
|
+
except PermissionError:
|
1251
|
+
print(f"Warning: Unable to change owner/group of {local_file}. Operation requires elevated privileges.")
|
1257
1252
|
|
1258
|
-
def file_upload(self, local_file: str, file_path: str, apply_file_attributes: bool = True) ->
|
1253
|
+
def file_upload(self, local_file: str, file_path: str, apply_file_attributes: bool = True) -> None:
|
1259
1254
|
"""Upload a local file to the remote server. Optionally, the remote file will have the same permission as the local file.
|
1260
1255
|
|
1261
1256
|
Dependency:
|
@@ -1266,10 +1261,10 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1266
1261
|
local_file (str): the local file path.
|
1267
1262
|
file_path (str): the target remote file to be uploaded.
|
1268
1263
|
apply_file_attributes (bool): whether to upload file attributes (permissions, owner, group) along with the file.
|
1269
|
-
|
1270
|
-
Returns:
|
1271
|
-
bool: success or failure.
|
1272
1264
|
"""
|
1265
|
+
if not os.path.exists(local_file):
|
1266
|
+
raise FileNotFoundError(f"Local file not found: {local_file}")
|
1267
|
+
|
1273
1268
|
from requests_toolbelt import MultipartEncoder
|
1274
1269
|
|
1275
1270
|
with open(file=local_file, mode="rb") as fp:
|
@@ -1291,9 +1286,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1291
1286
|
header=header,
|
1292
1287
|
body=encoder,
|
1293
1288
|
)
|
1294
|
-
|
1295
|
-
raise Exception(resp.text)
|
1296
|
-
return True
|
1289
|
+
resp.raise_for_status()
|
1297
1290
|
|
1298
1291
|
########################################
|
1299
1292
|
# Application run
|
@@ -1450,11 +1443,11 @@ class AppMeshClientTCP(AppMeshClient):
|
|
1450
1443
|
"""
|
1451
1444
|
Client SDK for interacting with the App Mesh service over TCP, with enhanced support for large file transfers.
|
1452
1445
|
|
1453
|
-
The `AppMeshClientTCP` class extends the functionality of `AppMeshClient` by offering a TCP-based communication layer
|
1454
|
-
for the App Mesh REST API. It overrides the file download and upload methods to support large file transfers with
|
1446
|
+
The `AppMeshClientTCP` class extends the functionality of `AppMeshClient` by offering a TCP-based communication layer
|
1447
|
+
for the App Mesh REST API. It overrides the file download and upload methods to support large file transfers with
|
1455
1448
|
improved performance, leveraging TCP for lower latency and higher throughput compared to HTTP.
|
1456
1449
|
|
1457
|
-
This client is suitable for applications requiring efficient data transfers and high-throughput operations within the
|
1450
|
+
This client is suitable for applications requiring efficient data transfers and high-throughput operations within the
|
1458
1451
|
App Mesh ecosystem, while maintaining compatibility with all other attributes and methods from `AppMeshClient`.
|
1459
1452
|
|
1460
1453
|
Dependency:
|
@@ -1482,7 +1475,7 @@ class AppMeshClientTCP(AppMeshClient):
|
|
1482
1475
|
|
1483
1476
|
def __init__(
|
1484
1477
|
self,
|
1485
|
-
rest_ssl_verify=
|
1478
|
+
rest_ssl_verify=DEFAULT_SSL_CA_PEM_FILE if os.path.exists(DEFAULT_SSL_CA_PEM_FILE) else False,
|
1486
1479
|
rest_ssl_client_cert=None,
|
1487
1480
|
jwt_token=None,
|
1488
1481
|
tcp_address=("localhost", 6059),
|
@@ -1664,31 +1657,34 @@ class AppMeshClientTCP(AppMeshClient):
|
|
1664
1657
|
########################################
|
1665
1658
|
# File management
|
1666
1659
|
########################################
|
1667
|
-
def file_download(self, file_path: str, local_file: str) ->
|
1660
|
+
def file_download(self, file_path: str, local_file: str, apply_file_attributes: bool = True) -> None:
|
1668
1661
|
"""Copy a remote file to local, the local file will have the same permission as the remote file
|
1669
1662
|
|
1670
1663
|
Args:
|
1671
1664
|
file_path (str): the remote file path.
|
1672
1665
|
local_file (str): the local file path to be downloaded.
|
1673
|
-
|
1674
|
-
Returns:
|
1675
|
-
bool: success or failure.
|
1666
|
+
apply_file_attributes (bool): whether to apply file attributes (permissions, owner, group) to the local file.
|
1676
1667
|
"""
|
1677
|
-
header = {}
|
1678
|
-
header["File-Path"] = file_path
|
1668
|
+
header = {"File-Path": file_path}
|
1679
1669
|
header[HTTP_HEADER_KEY_X_RECV_FILE_SOCKET] = "true"
|
1680
1670
|
resp = self._request_http(AppMeshClient.Method.GET, path="/appmesh/file/download", header=header)
|
1681
|
-
|
1682
|
-
|
1683
|
-
|
1671
|
+
|
1672
|
+
resp.raise_for_status()
|
1673
|
+
if HTTP_HEADER_KEY_X_RECV_FILE_SOCKET not in resp.headers:
|
1674
|
+
raise ValueError(f"Server did not respond with socket transfer option: {HTTP_HEADER_KEY_X_RECV_FILE_SOCKET}")
|
1675
|
+
|
1676
|
+
with open(local_file, "wb") as fp:
|
1677
|
+
chunk_data = bytes()
|
1678
|
+
chunk_size = int.from_bytes(self.__recvall(TCP_MESSAGE_HEADER_LENGTH), byteorder="big", signed=False)
|
1679
|
+
while chunk_size > 0:
|
1680
|
+
chunk_data = self.__recvall(chunk_size)
|
1681
|
+
if chunk_data is None or len(chunk_data) == 0:
|
1682
|
+
self.__close_socket()
|
1683
|
+
raise Exception("socket connection broken")
|
1684
|
+
fp.write(chunk_data)
|
1684
1685
|
chunk_size = int.from_bytes(self.__recvall(TCP_MESSAGE_HEADER_LENGTH), byteorder="big", signed=False)
|
1685
|
-
|
1686
|
-
|
1687
|
-
if chunk_data is None or len(chunk_data) == 0:
|
1688
|
-
self.__close_socket()
|
1689
|
-
raise Exception("socket connection broken")
|
1690
|
-
fp.write(chunk_data)
|
1691
|
-
chunk_size = int.from_bytes(self.__recvall(TCP_MESSAGE_HEADER_LENGTH), byteorder="big", signed=False)
|
1686
|
+
|
1687
|
+
if apply_file_attributes:
|
1692
1688
|
if "File-Mode" in resp.headers:
|
1693
1689
|
os.chmod(path=local_file, mode=int(resp.headers["File-Mode"]))
|
1694
1690
|
if "File-User" in resp.headers and "File-Group" in resp.headers:
|
@@ -1696,12 +1692,10 @@ class AppMeshClientTCP(AppMeshClient):
|
|
1696
1692
|
file_gid = int(resp.headers["File-Group"])
|
1697
1693
|
try:
|
1698
1694
|
os.chown(path=local_file, uid=file_uid, gid=file_gid)
|
1699
|
-
except
|
1700
|
-
print(
|
1701
|
-
return True
|
1702
|
-
return False
|
1695
|
+
except PermissionError:
|
1696
|
+
print(f"Warning: Unable to change owner/group of {local_file}. Operation requires elevated privileges.")
|
1703
1697
|
|
1704
|
-
def file_upload(self, local_file: str, file_path: str):
|
1698
|
+
def file_upload(self, local_file: str, file_path: str, apply_file_attributes: bool = True) -> None:
|
1705
1699
|
"""Upload a local file to the remote server, the remote file will have the same permission as the local file
|
1706
1700
|
|
1707
1701
|
Dependency:
|
@@ -1711,29 +1705,35 @@ class AppMeshClientTCP(AppMeshClient):
|
|
1711
1705
|
Args:
|
1712
1706
|
local_file (str): the local file path.
|
1713
1707
|
file_path (str): the target remote file to be uploaded.
|
1714
|
-
|
1715
|
-
Returns:
|
1716
|
-
bool: success or failure.
|
1717
|
-
str: text message.
|
1708
|
+
apply_file_attributes (bool): whether to upload file attributes (permissions, owner, group) along with the file.
|
1718
1709
|
"""
|
1710
|
+
if not os.path.exists(local_file):
|
1711
|
+
raise FileNotFoundError(f"Local file not found: {local_file}")
|
1712
|
+
|
1719
1713
|
with open(file=local_file, mode="rb") as fp:
|
1720
|
-
|
1721
|
-
header = {}
|
1722
|
-
header["File-Path"] = file_path
|
1723
|
-
header["File-Mode"] = str(file_stat.st_mode)
|
1724
|
-
header["File-User"] = str(file_stat.st_uid)
|
1725
|
-
header["File-Group"] = str(file_stat.st_gid)
|
1726
|
-
header["Content-Type"] = "text/plain"
|
1714
|
+
header = {"File-Path": file_path, "Content-Type": "text/plain"}
|
1727
1715
|
header[HTTP_HEADER_KEY_X_SEND_FILE_SOCKET] = "true"
|
1716
|
+
|
1717
|
+
if apply_file_attributes:
|
1718
|
+
file_stat = os.stat(local_file)
|
1719
|
+
header["File-Mode"] = str(file_stat.st_mode)
|
1720
|
+
header["File-User"] = str(file_stat.st_uid)
|
1721
|
+
header["File-Group"] = str(file_stat.st_gid)
|
1722
|
+
|
1728
1723
|
# https://stackoverflow.com/questions/22567306/python-requests-file-upload
|
1729
1724
|
resp = self._request_http(AppMeshClient.Method.POST, path="/appmesh/file/upload", header=header)
|
1730
|
-
|
1731
|
-
|
1725
|
+
|
1726
|
+
resp.raise_for_status()
|
1727
|
+
if HTTP_HEADER_KEY_X_SEND_FILE_SOCKET not in resp.headers:
|
1728
|
+
raise ValueError(f"Server did not respond with socket transfer option: {HTTP_HEADER_KEY_X_SEND_FILE_SOCKET}")
|
1729
|
+
|
1730
|
+
# TLS-optimized chunk size (slightly less than maximum TLS record size)
|
1731
|
+
# leaves some room for TLS overhead (like headers) within the 16 KB limit.
|
1732
|
+
chunk_size = 16 * 1024 - 512 # target to 16KB
|
1733
|
+
while True:
|
1732
1734
|
chunk_data = fp.read(chunk_size)
|
1733
|
-
|
1734
|
-
self.__socket_client.sendall(
|
1735
|
-
|
1736
|
-
|
1737
|
-
self.__socket_client.sendall(
|
1738
|
-
return True, ""
|
1739
|
-
return False, resp.json()[REST_TEXT_MESSAGE_JSON_KEY]
|
1735
|
+
if not chunk_data:
|
1736
|
+
self.__socket_client.sendall((0).to_bytes(TCP_MESSAGE_HEADER_LENGTH, byteorder="big", signed=False))
|
1737
|
+
break
|
1738
|
+
self.__socket_client.sendall(len(chunk_data).to_bytes(TCP_MESSAGE_HEADER_LENGTH, byteorder="big", signed=False))
|
1739
|
+
self.__socket_client.sendall(chunk_data)
|
@@ -0,0 +1,6 @@
|
|
1
|
+
appmesh/__init__.py,sha256=xRdXeFHEieRauuJZElbEBASgXG0ZzU1a5_0isAhM7Gw,11
|
2
|
+
appmesh/appmesh_client.py,sha256=6lnfsR6SAhF66jWBPIVPbC43LPhyAL3amBHxSx30Cu4,68327
|
3
|
+
appmesh-1.3.4.dist-info/METADATA,sha256=TZjYqpu2P33ZcN7ue8uTAMobwavbud7_zr9Eo2si7NY,11191
|
4
|
+
appmesh-1.3.4.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
5
|
+
appmesh-1.3.4.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
|
6
|
+
appmesh-1.3.4.dist-info/RECORD,,
|
appmesh-1.3.3.dist-info/RECORD
DELETED
@@ -1,6 +0,0 @@
|
|
1
|
-
appmesh/__init__.py,sha256=xRdXeFHEieRauuJZElbEBASgXG0ZzU1a5_0isAhM7Gw,11
|
2
|
-
appmesh/appmesh_client.py,sha256=Hsp4at6YCLcAoz8eRx928amyCTqN4ZAIFnVHxEbzDrI,67720
|
3
|
-
appmesh-1.3.3.dist-info/METADATA,sha256=73_wYNYMLoHLoew35YGTd2bGKWhfScEObKhr07yyHx0,11191
|
4
|
-
appmesh-1.3.3.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
5
|
-
appmesh-1.3.3.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
|
6
|
-
appmesh-1.3.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|