appmesh 1.6.13__py3-none-any.whl → 1.6.14__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/app.py +28 -18
- appmesh/client_http.py +47 -61
- appmesh/client_http_oauth.py +2 -2
- appmesh/client_tcp.py +158 -111
- appmesh/server_http.py +20 -23
- appmesh/server_tcp.py +4 -8
- appmesh/tcp_transport.py +0 -2
- {appmesh-1.6.13.dist-info → appmesh-1.6.14.dist-info}/METADATA +1 -1
- appmesh-1.6.14.dist-info/RECORD +16 -0
- appmesh-1.6.13.dist-info/RECORD +0 -16
- {appmesh-1.6.13.dist-info → appmesh-1.6.14.dist-info}/WHEEL +0 -0
- {appmesh-1.6.13.dist-info → appmesh-1.6.14.dist-info}/top_level.txt +0 -0
appmesh/app.py
CHANGED
@@ -134,75 +134,85 @@ class App:
|
|
134
134
|
|
135
135
|
# Application configuration
|
136
136
|
self.name = _get_str(data, "name")
|
137
|
-
"""
|
137
|
+
"""app name (unique)"""
|
138
138
|
self.command = _get_str(data, "command")
|
139
139
|
"""full command line with arguments"""
|
140
140
|
self.shell = _get_bool(data, "shell")
|
141
|
-
"""
|
141
|
+
"""Whether run command in shell mode (enables shell syntax such as pipes and compound commands)"""
|
142
142
|
self.session_login = _get_bool(data, "session_login")
|
143
|
-
"""
|
143
|
+
"""Whether to run the app in session login mode (inheriting the user's full login environment)"""
|
144
144
|
self.description = _get_str(data, "description")
|
145
|
-
"""
|
145
|
+
"""app description string"""
|
146
146
|
self.metadata = _get_item(data, "metadata")
|
147
|
-
"""metadata string/JSON (input for
|
147
|
+
"""metadata string/JSON (input for app, pass to process stdin)"""
|
148
148
|
self.working_dir = _get_str(data, "working_dir")
|
149
149
|
"""working directory"""
|
150
150
|
self.status = _get_int(data, "status")
|
151
|
-
"""
|
151
|
+
"""app status: 1 for enabled, 0 for disabled"""
|
152
152
|
self.docker_image = _get_str(data, "docker_image")
|
153
|
-
"""
|
153
|
+
"""Docker image for containerized execution"""
|
154
154
|
self.stdout_cache_num = _get_int(data, "stdout_cache_num")
|
155
|
-
"""stdout
|
155
|
+
"""maximum number of stdout log files to retain"""
|
156
156
|
self.start_time = _get_int(data, "start_time")
|
157
157
|
"""start date time for app (ISO8601 time format, e.g., '2020-10-11T09:22:05')"""
|
158
158
|
self.end_time = _get_int(data, "end_time")
|
159
159
|
"""end date time for app (ISO8601 time format, e.g., '2020-10-11T10:22:05')"""
|
160
|
-
self.
|
160
|
+
self.start_interval_seconds = _get_int(data, "start_interval_seconds")
|
161
161
|
"""start interval seconds for short running app, support ISO 8601 durations and cron expression (e.g., 'P1Y2M3DT4H5M6S' 'P5W' '* */5 * * * *')"""
|
162
162
|
self.cron = _get_bool(data, "cron")
|
163
|
-
"""
|
163
|
+
"""Whether the interval is specified as a cron expression"""
|
164
164
|
self.daily_limitation = App.DailyLimitation(_get_item(data, "daily_limitation"))
|
165
165
|
self.retention = _get_str(data, "retention")
|
166
166
|
"""extra timeout seconds for stopping current process, support ISO 8601 durations (e.g., 'P1Y2M3DT4H5M6S' 'P5W')."""
|
167
167
|
self.health_check_cmd = _get_str(data, "health_check_cmd")
|
168
168
|
"""health check script command (e.g., sh -x 'curl host:port/health', return 0 is health)"""
|
169
169
|
self.permission = _get_int(data, "permission")
|
170
|
-
"""
|
170
|
+
"""app user permission, value is 2 bit integer: [group & other], each bit can be deny:1, read:2, write: 3."""
|
171
171
|
self.behavior = App.Behavior(_get_item(data, "behavior"))
|
172
172
|
|
173
173
|
self.env = data.get("env", {}) if data else {}
|
174
174
|
"""environment variables (e.g., -e env1=value1 -e env2=value2, APP_DOCKER_OPTS is used to input docker run parameters)"""
|
175
175
|
self.sec_env = data.get("sec_env", {}) if data else {}
|
176
|
-
"""security environment variables, encrypt in server side with
|
176
|
+
"""security environment variables, encrypt in server side with app owner's cipher"""
|
177
177
|
self.pid = _get_int(data, "pid")
|
178
178
|
"""process id used to attach to the running process"""
|
179
179
|
self.resource_limit = App.ResourceLimitation(_get_item(data, "resource_limit"))
|
180
180
|
|
181
181
|
# Read-only attributes
|
182
|
+
self.register_time = _get_int(data, "register_time")
|
183
|
+
"""app register time"""
|
184
|
+
self.starts = _get_int(data, "starts")
|
185
|
+
"""number of times started"""
|
182
186
|
self.owner = _get_str(data, "owner")
|
183
|
-
"""owner name"""
|
187
|
+
"""owner name of app mesh user who created the app"""
|
184
188
|
self.user = _get_str(data, "pid_user")
|
185
|
-
"""process user name"""
|
189
|
+
"""process OS user name"""
|
186
190
|
self.pstree = _get_str(data, "pstree")
|
187
191
|
"""process tree"""
|
188
192
|
self.container_id = _get_str(data, "container_id")
|
189
|
-
"""container id"""
|
193
|
+
"""docker container id"""
|
190
194
|
self.memory = _get_int(data, "memory")
|
191
195
|
"""memory usage"""
|
192
196
|
self.cpu = _get_int(data, "cpu")
|
193
197
|
"""cpu usage"""
|
194
198
|
self.fd = _get_int(data, "fd")
|
195
199
|
"""file descriptor usage"""
|
200
|
+
self.stdout_cache_size = _get_int(data, "stdout_cache_size")
|
201
|
+
"""number of stdout log files currently retained"""
|
196
202
|
self.last_start_time = _get_int(data, "last_start_time")
|
197
203
|
"""last start time"""
|
198
204
|
self.last_exit_time = _get_int(data, "last_exit_time")
|
199
205
|
"""last exit time"""
|
206
|
+
self.last_error = _get_str(data, "last_error")
|
207
|
+
"""last error message"""
|
208
|
+
self.next_start_time = _get_int(data, "next_start_time")
|
209
|
+
"""next start time"""
|
200
210
|
self.health = _get_int(data, "health")
|
201
|
-
"""health status"""
|
211
|
+
"""health status: 0 for healthy, 1 for unhealthy"""
|
202
212
|
self.version = _get_int(data, "version")
|
203
|
-
"""version
|
213
|
+
"""app version"""
|
204
214
|
self.return_code = _get_int(data, "return_code")
|
205
|
-
"""last exit code"""
|
215
|
+
"""last process exit code"""
|
206
216
|
self.task_id = _get_int(data, "task_id")
|
207
217
|
"""current task id"""
|
208
218
|
self.task_status = _get_str(data, "task_status")
|
appmesh/client_http.py
CHANGED
@@ -12,6 +12,7 @@ import os
|
|
12
12
|
import sys
|
13
13
|
import threading
|
14
14
|
import time
|
15
|
+
from pathlib import Path
|
15
16
|
from datetime import datetime
|
16
17
|
from enum import Enum, unique
|
17
18
|
from http import HTTPStatus
|
@@ -39,17 +40,6 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
39
40
|
|
40
41
|
This client is designed for direct usage in applications that require access to App Mesh services over HTTP-based REST.
|
41
42
|
|
42
|
-
Usage:
|
43
|
-
- Install the App Mesh Python package:
|
44
|
-
python3 -m pip install --upgrade appmesh
|
45
|
-
- Import the client module:
|
46
|
-
from appmesh import AppMeshClient
|
47
|
-
|
48
|
-
Example:
|
49
|
-
client = AppMeshClient()
|
50
|
-
client.login("your-name", "your-password")
|
51
|
-
response = client.app_view(app_name='ping')
|
52
|
-
|
53
43
|
Attributes:
|
54
44
|
- TLS (Transport Layer Security): Supports secure connections between the client and App Mesh service,
|
55
45
|
ensuring encrypted communication.
|
@@ -112,6 +102,13 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
112
102
|
- update_role()
|
113
103
|
- view_roles()
|
114
104
|
- view_groups()
|
105
|
+
|
106
|
+
Example:
|
107
|
+
>>> python -m pip install --upgrade appmesh
|
108
|
+
>>> from appmesh import AppMeshClient
|
109
|
+
>>> client = AppMeshClient()
|
110
|
+
>>> client.login("your-name", "your-password")
|
111
|
+
>>> response = client.app_view(app_name='ping')
|
115
112
|
"""
|
116
113
|
|
117
114
|
DURATION_ONE_WEEK_ISO = "P1W"
|
@@ -121,10 +118,10 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
121
118
|
TOKEN_REFRESH_OFFSET = 30 # 30s before token expire to refresh token
|
122
119
|
|
123
120
|
# Platform-aware default SSL paths
|
124
|
-
_DEFAULT_SSL_DIR = "
|
125
|
-
DEFAULT_SSL_CA_CERT_PATH =
|
126
|
-
DEFAULT_SSL_CLIENT_CERT_PATH =
|
127
|
-
DEFAULT_SSL_CLIENT_KEY_PATH =
|
121
|
+
_DEFAULT_SSL_DIR = Path("C:/local/appmesh/ssl") if os.name == "nt" else Path("/opt/appmesh/ssl")
|
122
|
+
DEFAULT_SSL_CA_CERT_PATH = str(_DEFAULT_SSL_DIR / "ca.pem")
|
123
|
+
DEFAULT_SSL_CLIENT_CERT_PATH = str(_DEFAULT_SSL_DIR / "client.pem")
|
124
|
+
DEFAULT_SSL_CLIENT_KEY_PATH = str(_DEFAULT_SSL_DIR / "client-key.pem")
|
128
125
|
|
129
126
|
DEFAULT_JWT_AUDIENCE = "appmesh-service"
|
130
127
|
|
@@ -199,39 +196,28 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
199
196
|
def __init__(
|
200
197
|
self,
|
201
198
|
rest_url: str = "https://127.0.0.1:6060",
|
202
|
-
rest_ssl_verify
|
203
|
-
rest_ssl_client_cert=(DEFAULT_SSL_CLIENT_CERT_PATH, DEFAULT_SSL_CLIENT_KEY_PATH)
|
204
|
-
rest_timeout=(60, 300),
|
199
|
+
rest_ssl_verify: Union[bool, str] = DEFAULT_SSL_CA_CERT_PATH,
|
200
|
+
rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = (DEFAULT_SSL_CLIENT_CERT_PATH, DEFAULT_SSL_CLIENT_KEY_PATH),
|
201
|
+
rest_timeout: Tuple[float, float] = (60, 300),
|
205
202
|
jwt_token: Optional[str] = None,
|
206
203
|
rest_cookie_file: Optional[str] = None,
|
207
|
-
auto_refresh_token=False,
|
204
|
+
auto_refresh_token: bool = False,
|
208
205
|
):
|
209
206
|
"""Initialize an App Mesh HTTP client for interacting with the App Mesh server via secure HTTPS.
|
210
207
|
|
211
208
|
Args:
|
212
|
-
rest_url
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
rest_timeout (tuple, optional): HTTP connection timeouts for API requests, as `(connect_timeout, read_timeout)`.
|
226
|
-
The default is `(60, 300)`, where `60` seconds is the maximum time to establish a connection and `300` seconds for the maximum read duration.
|
227
|
-
|
228
|
-
rest_cookie_file (str, optional): Path to a file for storing session cookies.
|
229
|
-
If provided, cookies will be saved to and loaded from this file to maintain session state across client instances instead of keep jwt_token.
|
230
|
-
|
231
|
-
jwt_token (str, optional): JWT token for API authentication, used in headers to authorize requests where required.
|
232
|
-
auto_refresh_token (bool, optional): Enable automatic token refresh before expiration.
|
233
|
-
When enabled, a background timer will monitor token expiration and attempt to refresh
|
234
|
-
the token before it expires. This works with both native App Mesh tokens and Keycloak tokens.
|
209
|
+
rest_url: Base server URI (protocol, host, port). Default: `"https://127.0.0.1:6060"`.
|
210
|
+
rest_ssl_verify: SSL verification mode for HTTPS requests:
|
211
|
+
- True: Use system CAs.
|
212
|
+
- False: Disable verification (insecure).
|
213
|
+
- str: Path to custom CA or directory. To include system CAs, combine them into one file (e.g., cat custom_ca.pem /etc/ssl/certs/ca-certificates.crt > combined_ca.pem).
|
214
|
+
rest_ssl_client_cert: SSL client certificate:
|
215
|
+
- str: Path to single PEM with cert+key
|
216
|
+
- tuple: (cert_path, key_path)
|
217
|
+
rest_timeout: Timeouts `(connect, read)` in seconds. Default `(60, 300)`.
|
218
|
+
rest_cookie_file: Cookie file path for session persistence (alternative to jwt_token).
|
219
|
+
jwt_token: JWT token for API authentication, overrides cookie file if both provided.
|
220
|
+
auto_refresh_token: Auto-refresh JWT before expiry (supports App Mesh and Keycloak tokens).
|
235
221
|
"""
|
236
222
|
self._ensure_logging_configured()
|
237
223
|
self.auth_server_url = rest_url
|
@@ -1202,13 +1188,13 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1202
1188
|
########################################
|
1203
1189
|
# File management
|
1204
1190
|
########################################
|
1205
|
-
def download_file(self, remote_file: str, local_file: str,
|
1191
|
+
def download_file(self, remote_file: str, local_file: str, preserve_permissions: bool = True) -> None:
|
1206
1192
|
"""Download a remote file to the local system. Optionally, the local file will have the same permission as the remote file.
|
1207
1193
|
|
1208
1194
|
Args:
|
1209
1195
|
remote_file (str): the remote file path.
|
1210
1196
|
local_file (str): the local file path to be downloaded.
|
1211
|
-
|
1197
|
+
preserve_permissions (bool): whether to apply file attributes (permissions, owner, group) to the local file.
|
1212
1198
|
"""
|
1213
1199
|
resp = self._request_http(AppMeshClient.Method.GET, path="/appmesh/file/download", header={self.HTTP_HEADER_KEY_X_FILE_PATH: remote_file})
|
1214
1200
|
resp.raise_for_status()
|
@@ -1220,7 +1206,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1220
1206
|
fp.write(chunk)
|
1221
1207
|
|
1222
1208
|
# Apply file attributes (permissions, owner, group) if requested
|
1223
|
-
if
|
1209
|
+
if preserve_permissions and sys.platform != "win32":
|
1224
1210
|
if "X-File-Mode" in resp.headers:
|
1225
1211
|
os.chmod(path=local_file, mode=int(resp.headers["X-File-Mode"]))
|
1226
1212
|
if "X-File-User" in resp.headers and "X-File-Group" in resp.headers:
|
@@ -1231,7 +1217,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1231
1217
|
except PermissionError:
|
1232
1218
|
logging.warning(f"Warning: Unable to change owner/group of {local_file}. Operation requires elevated privileges.")
|
1233
1219
|
|
1234
|
-
def upload_file(self, local_file: str, remote_file: str,
|
1220
|
+
def upload_file(self, local_file: str, remote_file: str, preserve_permissions: bool = True) -> None:
|
1235
1221
|
"""Upload a local file to the remote server. Optionally, the remote file will have the same permission as the local file.
|
1236
1222
|
|
1237
1223
|
Dependency:
|
@@ -1241,7 +1227,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1241
1227
|
Args:
|
1242
1228
|
local_file (str): the local file path.
|
1243
1229
|
remote_file (str): the target remote file to be uploaded.
|
1244
|
-
|
1230
|
+
preserve_permissions (bool): whether to upload file attributes (permissions, owner, group) along with the file.
|
1245
1231
|
"""
|
1246
1232
|
if not os.path.exists(local_file):
|
1247
1233
|
raise FileNotFoundError(f"Local file not found: {local_file}")
|
@@ -1253,7 +1239,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1253
1239
|
header = {self.HTTP_HEADER_KEY_X_FILE_PATH: parse.quote(remote_file), "Content-Type": encoder.content_type}
|
1254
1240
|
|
1255
1241
|
# Include file attributes (permissions, owner, group) if requested
|
1256
|
-
if
|
1242
|
+
if preserve_permissions:
|
1257
1243
|
file_stat = os.stat(local_file)
|
1258
1244
|
header["X-File-Mode"] = str(file_stat.st_mode & 0o777) # Mask to keep only permission bits
|
1259
1245
|
header["X-File-User"] = str(file_stat.st_uid)
|
@@ -1486,24 +1472,24 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1486
1472
|
header.setdefault("Content-Type", "application/json")
|
1487
1473
|
|
1488
1474
|
try:
|
1475
|
+
request_kwargs = {
|
1476
|
+
"url": rest_url,
|
1477
|
+
"headers": header,
|
1478
|
+
"cert": self.ssl_client_cert,
|
1479
|
+
"verify": self.ssl_verify,
|
1480
|
+
"timeout": self.rest_timeout,
|
1481
|
+
}
|
1482
|
+
|
1489
1483
|
if method is AppMeshClient.Method.GET:
|
1490
|
-
resp = self.session.get(
|
1484
|
+
resp = self.session.get(params=query, **request_kwargs)
|
1491
1485
|
elif method is AppMeshClient.Method.POST:
|
1492
|
-
resp = self.session.post(
|
1493
|
-
url=rest_url,
|
1494
|
-
params=query,
|
1495
|
-
headers=header,
|
1496
|
-
data=body,
|
1497
|
-
cert=self.ssl_client_cert,
|
1498
|
-
verify=self.ssl_verify,
|
1499
|
-
timeout=self.rest_timeout,
|
1500
|
-
)
|
1486
|
+
resp = self.session.post(params=query, data=body, **request_kwargs)
|
1501
1487
|
elif method is AppMeshClient.Method.POST_STREAM:
|
1502
|
-
resp = self.session.post(
|
1488
|
+
resp = self.session.post(params=query, data=body, stream=True, **request_kwargs)
|
1503
1489
|
elif method is AppMeshClient.Method.DELETE:
|
1504
|
-
resp = self.session.delete(
|
1490
|
+
resp = self.session.delete(**request_kwargs)
|
1505
1491
|
elif method is AppMeshClient.Method.PUT:
|
1506
|
-
resp = self.session.put(
|
1492
|
+
resp = self.session.put(params=query, data=body, **request_kwargs)
|
1507
1493
|
else:
|
1508
1494
|
raise Exception("Invalid http method", method)
|
1509
1495
|
|
appmesh/client_http_oauth.py
CHANGED
@@ -18,8 +18,8 @@ class AppMeshClientOAuth(AppMeshClient):
|
|
18
18
|
self,
|
19
19
|
oauth2: dict, # Required for Keycloak
|
20
20
|
rest_url: str = "https://127.0.0.1:6060",
|
21
|
-
rest_ssl_verify=AppMeshClient.DEFAULT_SSL_CA_CERT_PATH
|
22
|
-
rest_ssl_client_cert=
|
21
|
+
rest_ssl_verify=AppMeshClient.DEFAULT_SSL_CA_CERT_PATH,
|
22
|
+
rest_ssl_client_cert=AppMeshClient.DEFAULT_SSL_CLIENT_CERT_PATH,
|
23
23
|
rest_timeout=(60, 300),
|
24
24
|
jwt_token=None, # Keycloak dict
|
25
25
|
auto_refresh_token: bool = True, # Default to True for Keycloak
|
appmesh/client_tcp.py
CHANGED
@@ -7,6 +7,7 @@ import os
|
|
7
7
|
import socket
|
8
8
|
import sys
|
9
9
|
import uuid
|
10
|
+
from typing import Optional, Tuple, Union
|
10
11
|
|
11
12
|
# Third-party imports
|
12
13
|
import requests
|
@@ -18,8 +19,7 @@ from .tcp_transport import TCPTransport
|
|
18
19
|
|
19
20
|
|
20
21
|
class AppMeshClientTCP(AppMeshClient):
|
21
|
-
"""
|
22
|
-
Client SDK for interacting with the App Mesh service over TCP, with enhanced support for large file transfers.
|
22
|
+
"""Client SDK for interacting with the App Mesh service over TCP.
|
23
23
|
|
24
24
|
The `AppMeshClientTCP` class extends the functionality of `AppMeshClient` by offering a TCP-based communication layer
|
25
25
|
for the App Mesh REST API. It overrides the file download and upload methods to support large file transfers with
|
@@ -28,30 +28,23 @@ class AppMeshClientTCP(AppMeshClient):
|
|
28
28
|
This client is suitable for applications requiring efficient data transfers and high-throughput operations within the
|
29
29
|
App Mesh ecosystem, while maintaining compatibility with all other attributes and methods from `AppMeshClient`.
|
30
30
|
|
31
|
-
Dependency:
|
32
|
-
- Install the required package for message serialization:
|
33
|
-
pip3 install msgpack
|
34
|
-
|
35
|
-
Usage:
|
36
|
-
- Import the client module:
|
37
|
-
from appmesh import AppMeshClientTCP
|
38
|
-
|
39
|
-
Example:
|
40
|
-
client = AppMeshClientTCP()
|
41
|
-
client.login("your-name", "your-password")
|
42
|
-
client.file_download("/tmp/os-release", "os-release")
|
43
|
-
|
44
31
|
Attributes:
|
45
|
-
|
46
|
-
- Optimized for TCP-based communication to provide better performance for large file transfers.
|
32
|
+
Inherits all attributes from `AppMeshClient`, including TLS secure connections and JWT-based authentication.
|
47
33
|
|
48
34
|
Methods:
|
49
|
-
-
|
50
|
-
-
|
35
|
+
- download_file()
|
36
|
+
- upload_file()
|
51
37
|
- Inherits all other methods from `AppMeshClient`, providing a consistent interface for managing applications within App Mesh.
|
38
|
+
|
39
|
+
Example:
|
40
|
+
>>> from appmesh import AppMeshClientTCP
|
41
|
+
>>> client = AppMeshClientTCP()
|
42
|
+
>>> client.login("your-name", "your-password")
|
43
|
+
>>> client.download_file("/tmp/os-release", "os-release")
|
52
44
|
"""
|
53
45
|
|
54
|
-
|
46
|
+
# TLS-optimized chunk size, leaves room for TLS overhead within the 16 KB limit
|
47
|
+
TCP_BLOCK_SIZE = 16 * 1024 - 128
|
55
48
|
ENCODING_UTF8 = "utf-8"
|
56
49
|
HTTP_USER_AGENT_TCP = "appmesh/python/tcp"
|
57
50
|
HTTP_HEADER_KEY_X_SEND_FILE_SOCKET = "X-Send-File-Socket"
|
@@ -59,32 +52,27 @@ class AppMeshClientTCP(AppMeshClient):
|
|
59
52
|
|
60
53
|
def __init__(
|
61
54
|
self,
|
62
|
-
rest_ssl_verify=
|
63
|
-
rest_ssl_client_cert=None,
|
64
|
-
jwt_token=None,
|
65
|
-
tcp_address=("127.0.0.1", 6059),
|
55
|
+
rest_ssl_verify: Union[bool, str] = AppMeshClient.DEFAULT_SSL_CA_CERT_PATH,
|
56
|
+
rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = None,
|
57
|
+
jwt_token: Optional[str] = None,
|
58
|
+
tcp_address: Tuple[str, int] = ("127.0.0.1", 6059),
|
66
59
|
):
|
67
60
|
"""Construct an App Mesh client TCP object to communicate securely with an App Mesh server over TLS.
|
68
61
|
|
69
62
|
Args:
|
70
|
-
rest_ssl_verify
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
jwt_token (str, optional): JWT token for authentication. Used in methods requiring login and user authorization.
|
85
|
-
|
86
|
-
tcp_address (Tuple[str, int], optional): Address and port for establishing a TCP connection to the server.
|
87
|
-
Defaults to `("127.0.0.1", 6059)`.
|
63
|
+
rest_ssl_verify: SSL certificate verification behavior. Can be True, False, or a path to CA bundle.
|
64
|
+
- True: Use system CA certificates (e.g., /etc/ssl/certs/ on Linux)
|
65
|
+
- False: Disable verification (insecure)
|
66
|
+
- str: Path to custom CA bundle or directory
|
67
|
+
ssl_client_cert: SSL client certificate:
|
68
|
+
- str: Path to single PEM with cert+key
|
69
|
+
- tuple: (cert_path, key_path)
|
70
|
+
jwt_token: Pre-configured JWT Token for authenticating requests.
|
71
|
+
tcp_address: Server address as (host, port) tuple, defaults to ("127.0.0.1", 6059).
|
72
|
+
|
73
|
+
Note:
|
74
|
+
TCP connections require an explicit full-chain CA specification for certificate validation,
|
75
|
+
unlike HTTP, which can retrieve intermediate certificates automatically.
|
88
76
|
"""
|
89
77
|
self.tcp_transport = TCPTransport(address=tcp_address, ssl_verify=rest_ssl_verify, ssl_client_cert=rest_ssl_client_cert)
|
90
78
|
super().__init__(rest_ssl_verify=rest_ssl_verify, rest_ssl_client_cert=rest_ssl_client_cert, jwt_token=jwt_token)
|
@@ -102,93 +90,159 @@ class AppMeshClientTCP(AppMeshClient):
|
|
102
90
|
self.close()
|
103
91
|
except Exception:
|
104
92
|
pass # Never raise in __del__
|
105
|
-
super().__del__()
|
106
93
|
|
107
|
-
def
|
94
|
+
def _covert_bytes(self, body) -> bytes:
|
95
|
+
"""Prepare request body for transmission."""
|
96
|
+
if body is None:
|
97
|
+
return b""
|
98
|
+
|
99
|
+
if isinstance(body, (bytes, bytearray, memoryview)):
|
100
|
+
return body
|
101
|
+
|
102
|
+
if isinstance(body, str):
|
103
|
+
return body.encode(self.ENCODING_UTF8)
|
104
|
+
|
105
|
+
if isinstance(body, (dict, list)):
|
106
|
+
return json.dumps(body).encode(self.ENCODING_UTF8)
|
107
|
+
|
108
|
+
raise TypeError(f"Unsupported body type: {type(body)}")
|
109
|
+
|
110
|
+
def _request_http(self, method: AppMeshClient.Method, path: str, query: Optional[dict] = None, header: Optional[dict] = None, body=None) -> requests.Response:
|
108
111
|
"""Send HTTP request over TCP transport.
|
109
112
|
|
110
113
|
Args:
|
111
|
-
method
|
112
|
-
path
|
113
|
-
query
|
114
|
-
header
|
114
|
+
method: HTTP method.
|
115
|
+
path: URI path.
|
116
|
+
query: Query parameters.
|
117
|
+
header: HTTP headers.
|
115
118
|
body: Request body.
|
116
119
|
|
117
120
|
Returns:
|
118
|
-
|
121
|
+
Simulated HTTP response.
|
119
122
|
"""
|
123
|
+
|
124
|
+
# Check for unsupported features
|
125
|
+
if super().forward_to:
|
126
|
+
raise RuntimeError("Forward request is not supported in TCP mode")
|
127
|
+
|
120
128
|
if not self.tcp_transport.connected():
|
121
129
|
self.tcp_transport.connect()
|
122
130
|
|
131
|
+
# Prepare request message (ensure no fields are assigned None!)
|
123
132
|
appmesh_request = RequestMessage()
|
124
|
-
token = self._get_access_token()
|
125
|
-
if token:
|
126
|
-
appmesh_request.headers[self.HTTP_HEADER_KEY_AUTH] = token
|
127
|
-
if super().forward_to and len(super().forward_to) > 0:
|
128
|
-
raise Exception("Not support forward request in TCP mode")
|
129
|
-
appmesh_request.headers[self.HTTP_HEADER_KEY_USER_AGENT] = self.HTTP_USER_AGENT_TCP
|
130
133
|
appmesh_request.uuid = str(uuid.uuid1())
|
131
134
|
appmesh_request.http_method = method.value
|
132
135
|
appmesh_request.request_uri = path
|
133
136
|
appmesh_request.client_addr = socket.gethostname()
|
137
|
+
appmesh_request.headers[self.HTTP_HEADER_KEY_USER_AGENT] = self.HTTP_USER_AGENT_TCP
|
134
138
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
appmesh_request.body = bytes(body, self.ENCODING_UTF8)
|
140
|
-
elif isinstance(body, bytes):
|
141
|
-
appmesh_request.body = body
|
142
|
-
else:
|
143
|
-
raise Exception(f"UnSupported body type: {type(body)}")
|
139
|
+
# Add authentication token
|
140
|
+
token = self._get_access_token()
|
141
|
+
if token:
|
142
|
+
appmesh_request.headers[self.HTTP_HEADER_KEY_AUTH] = token
|
144
143
|
|
144
|
+
# Add custom headers
|
145
145
|
if header:
|
146
|
-
|
147
|
-
|
146
|
+
appmesh_request.headers.update(header)
|
147
|
+
|
148
|
+
# Add query parameters
|
148
149
|
if query:
|
149
|
-
|
150
|
-
|
150
|
+
appmesh_request.query.update(query)
|
151
|
+
|
152
|
+
# Prepare body
|
153
|
+
body_bytes = self._covert_bytes(body)
|
154
|
+
if body_bytes:
|
155
|
+
appmesh_request.body = body_bytes
|
151
156
|
|
157
|
+
# Send request
|
152
158
|
data = appmesh_request.serialize()
|
153
159
|
self.tcp_transport.send_message(data)
|
154
160
|
|
161
|
+
# Receive response
|
155
162
|
resp_data = self.tcp_transport.receive_message()
|
156
163
|
if not resp_data: # Covers None and empty bytes
|
157
164
|
self.tcp_transport.close()
|
158
|
-
raise
|
165
|
+
raise ConnectionError("Socket connection broken")
|
159
166
|
|
167
|
+
# Parse response
|
160
168
|
appmesh_resp = ResponseMessage().deserialize(resp_data)
|
161
169
|
response = requests.Response()
|
162
170
|
response.status_code = appmesh_resp.http_status
|
163
|
-
# response.encoding = self.ENCODING_UTF8 # only need when charset not in appmesh_resp.body_msg_type
|
164
|
-
response._content = appmesh_resp.body if isinstance(appmesh_resp.body, bytes) else str(appmesh_resp.body).encode(self.ENCODING_UTF8)
|
165
171
|
response.headers = appmesh_resp.headers
|
172
|
+
|
173
|
+
# Set response content
|
174
|
+
# response.encoding = self.ENCODING_UTF8 # only need when charset not in appmesh_resp.body_msg_type
|
175
|
+
if isinstance(appmesh_resp.body, bytes):
|
176
|
+
response._content = appmesh_resp.body
|
177
|
+
else:
|
178
|
+
response._content = str(appmesh_resp.body).encode(self.ENCODING_UTF8)
|
179
|
+
|
180
|
+
# Set content type
|
166
181
|
if appmesh_resp.body_msg_type:
|
167
182
|
response.headers["Content-Type"] = appmesh_resp.body_msg_type
|
168
183
|
|
169
184
|
return AppMeshClient.EncodingResponse(response)
|
170
185
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
186
|
+
def _apply_file_attributes(self, local_file: str, headers: dict) -> None:
|
187
|
+
"""Apply file attributes from headers to local file."""
|
188
|
+
if sys.platform == "win32":
|
189
|
+
return
|
190
|
+
|
191
|
+
# Apply file mode
|
192
|
+
if "X-File-Mode" in headers:
|
193
|
+
try:
|
194
|
+
os.chmod(local_file, int(headers["X-File-Mode"]))
|
195
|
+
except (ValueError, OSError) as e:
|
196
|
+
self._logger.warning("Failed to set file mode: %s", e)
|
197
|
+
|
198
|
+
# Apply file ownership
|
199
|
+
if "X-File-User" in headers and "X-File-Group" in headers:
|
200
|
+
try:
|
201
|
+
file_uid = int(headers["X-File-User"])
|
202
|
+
file_gid = int(headers["X-File-Group"])
|
203
|
+
os.chown(local_file, uid=file_uid, gid=file_gid)
|
204
|
+
except (ValueError, PermissionError, OSError) as e:
|
205
|
+
if isinstance(e, PermissionError):
|
206
|
+
print(f"Warning: Unable to change owner/group of {local_file}. " "Operation requires elevated privileges.")
|
207
|
+
else:
|
208
|
+
self._logger.warning("Failed to set file ownership: %s", e)
|
209
|
+
|
210
|
+
def _get_file_attributes(self, local_file: str) -> dict:
|
211
|
+
"""Get file attributes as header dictionary."""
|
212
|
+
if sys.platform == "win32":
|
213
|
+
return {}
|
214
|
+
|
215
|
+
try:
|
216
|
+
file_stat = os.stat(local_file)
|
217
|
+
return {
|
218
|
+
"X-File-Mode": str(file_stat.st_mode & 0o777),
|
219
|
+
"X-File-User": str(file_stat.st_uid),
|
220
|
+
"X-File-Group": str(file_stat.st_gid),
|
221
|
+
}
|
222
|
+
except OSError as e:
|
223
|
+
self._logger.warning("Failed to get file attributes: %s", e)
|
224
|
+
return {}
|
225
|
+
|
226
|
+
def download_file(self, remote_file: str, local_file: str, preserve_permissions: bool = True) -> None:
|
175
227
|
"""Copy a remote file to local, preserving file attributes if requested.
|
176
228
|
|
177
229
|
Args:
|
178
|
-
remote_file
|
179
|
-
local_file
|
180
|
-
|
230
|
+
remote_file: Remote file path.
|
231
|
+
local_file: Local destination path.
|
232
|
+
preserve_permissions: Apply remote file permissions/ownership locally.
|
181
233
|
"""
|
182
234
|
header = {
|
183
235
|
AppMeshClient.HTTP_HEADER_KEY_X_FILE_PATH: remote_file,
|
184
236
|
self.HTTP_HEADER_KEY_X_RECV_FILE_SOCKET: "true",
|
185
237
|
}
|
238
|
+
|
186
239
|
resp = self._request_http(AppMeshClient.Method.GET, path="/appmesh/file/download", header=header)
|
187
240
|
resp.raise_for_status()
|
188
241
|
|
189
242
|
if self.HTTP_HEADER_KEY_X_RECV_FILE_SOCKET not in resp.headers:
|
190
|
-
raise ValueError(f"Server did not respond with socket transfer option: {self.HTTP_HEADER_KEY_X_RECV_FILE_SOCKET}")
|
243
|
+
raise ValueError(f"Server did not respond with socket transfer option: " f"{self.HTTP_HEADER_KEY_X_RECV_FILE_SOCKET}")
|
191
244
|
|
245
|
+
# Download file chunks
|
192
246
|
with open(local_file, "wb") as fp:
|
193
247
|
while True:
|
194
248
|
chunk_data = self.tcp_transport.receive_message()
|
@@ -196,50 +250,43 @@ class AppMeshClientTCP(AppMeshClient):
|
|
196
250
|
break
|
197
251
|
fp.write(chunk_data)
|
198
252
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
file_gid = int(resp.headers["X-File-Group"])
|
205
|
-
try:
|
206
|
-
os.chown(path=local_file, uid=file_uid, gid=file_gid)
|
207
|
-
except PermissionError:
|
208
|
-
print(f"Warning: Unable to change owner/group of {local_file}. Operation requires elevated privileges.")
|
209
|
-
|
210
|
-
def upload_file(self, local_file: str, remote_file: str, apply_file_attributes: bool = True) -> None:
|
253
|
+
# Apply file attributes if requested
|
254
|
+
if preserve_permissions:
|
255
|
+
self._apply_file_attributes(local_file, resp.headers)
|
256
|
+
|
257
|
+
def upload_file(self, local_file: str, remote_file: str, preserve_permissions: bool = True) -> None:
|
211
258
|
"""Upload a local file to remote server, preserving file attributes if requested.
|
212
259
|
|
213
260
|
Args:
|
214
|
-
local_file
|
215
|
-
remote_file
|
216
|
-
|
261
|
+
local_file: Local file path.
|
262
|
+
remote_file: Remote destination path.
|
263
|
+
preserve_permissions: Upload file permissions/ownership metadata.
|
217
264
|
"""
|
218
265
|
if not os.path.exists(local_file):
|
219
266
|
raise FileNotFoundError(f"Local file not found: {local_file}")
|
220
267
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
268
|
+
# Prepare headers
|
269
|
+
header = {
|
270
|
+
AppMeshClient.HTTP_HEADER_KEY_X_FILE_PATH: remote_file,
|
271
|
+
"Content-Type": "text/plain",
|
272
|
+
self.HTTP_HEADER_KEY_X_SEND_FILE_SOCKET: "true",
|
273
|
+
}
|
227
274
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
header["X-File-User"] = str(file_stat.st_uid)
|
232
|
-
header["X-File-Group"] = str(file_stat.st_gid)
|
275
|
+
# Add file attributes if requested
|
276
|
+
if preserve_permissions:
|
277
|
+
header.update(self._get_file_attributes(local_file))
|
233
278
|
|
234
|
-
|
235
|
-
|
279
|
+
# Initiate upload
|
280
|
+
resp = self._request_http(AppMeshClient.Method.POST, path="/appmesh/file/upload", header=header)
|
281
|
+
resp.raise_for_status()
|
236
282
|
|
237
|
-
|
238
|
-
|
283
|
+
if self.HTTP_HEADER_KEY_X_SEND_FILE_SOCKET not in resp.headers:
|
284
|
+
raise ValueError(f"Server did not respond with socket transfer option: " f"{self.HTTP_HEADER_KEY_X_SEND_FILE_SOCKET}")
|
239
285
|
|
240
|
-
|
286
|
+
# Upload file chunks
|
287
|
+
with open(local_file, "rb") as fp:
|
241
288
|
while True:
|
242
|
-
chunk_data = fp.read(
|
289
|
+
chunk_data = fp.read(self.TCP_BLOCK_SIZE)
|
243
290
|
if not chunk_data:
|
244
291
|
self.tcp_transport.send_message([]) # EOF signal
|
245
292
|
break
|
appmesh/server_http.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# server_http.py
|
2
2
|
# pylint: disable=line-too-long,broad-exception-raised,broad-exception-caught,import-outside-toplevel,protected-access
|
3
3
|
|
4
|
+
"""HTTP server SDK implementation for App Mesh."""
|
5
|
+
|
4
6
|
# Standard library imports
|
5
7
|
import abc
|
6
8
|
import logging
|
@@ -16,8 +18,7 @@ logger = logging.getLogger(__name__)
|
|
16
18
|
|
17
19
|
|
18
20
|
class AppMeshServer(metaclass=abc.ABCMeta):
|
19
|
-
"""
|
20
|
-
Server SDK for App Mesh application interacting with the local App Mesh REST service over HTTPS.
|
21
|
+
"""Server SDK for App Mesh application interacting with the local App Mesh REST service over HTTPS.
|
21
22
|
|
22
23
|
Build-in runtime environment variables required:
|
23
24
|
- APP_MESH_PROCESS_KEY
|
@@ -34,18 +35,13 @@ class AppMeshServer(metaclass=abc.ABCMeta):
|
|
34
35
|
context.task_return(result)
|
35
36
|
"""
|
36
37
|
|
38
|
+
_RETRY_DELAY_SECONDS = 0.1
|
39
|
+
|
37
40
|
def __init__(
|
38
41
|
self,
|
39
42
|
rest_url: str = "https://127.0.0.1:6060",
|
40
|
-
rest_ssl_verify=
|
41
|
-
rest_ssl_client_cert=
|
42
|
-
(
|
43
|
-
AppMeshClient.DEFAULT_SSL_CLIENT_CERT_PATH,
|
44
|
-
AppMeshClient.DEFAULT_SSL_CLIENT_KEY_PATH,
|
45
|
-
)
|
46
|
-
if os.path.exists(AppMeshClient.DEFAULT_SSL_CLIENT_CERT_PATH)
|
47
|
-
else None
|
48
|
-
),
|
43
|
+
rest_ssl_verify: Union[bool, str] = AppMeshClient.DEFAULT_SSL_CA_CERT_PATH,
|
44
|
+
rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = None,
|
49
45
|
rest_timeout: Tuple[float, float] = (60, 300),
|
50
46
|
*,
|
51
47
|
logger_: Optional[logging.Logger] = None,
|
@@ -63,6 +59,7 @@ class AppMeshServer(metaclass=abc.ABCMeta):
|
|
63
59
|
"""Read and validate required runtime environment variables."""
|
64
60
|
process_key = os.getenv("APP_MESH_PROCESS_KEY")
|
65
61
|
app_name = os.getenv("APP_MESH_APPLICATION_NAME")
|
62
|
+
|
66
63
|
if not process_key:
|
67
64
|
raise Exception("Missing environment variable: APP_MESH_PROCESS_KEY. This must be set by App Mesh service.")
|
68
65
|
if not app_name:
|
@@ -75,43 +72,43 @@ class AppMeshServer(metaclass=abc.ABCMeta):
|
|
75
72
|
Used by App Mesh application process to obtain the payload from App Mesh service
|
76
73
|
that a client pushed to it.
|
77
74
|
|
78
|
-
|
79
75
|
Returns:
|
80
|
-
|
76
|
+
The payload provided by the client as returned by the service.
|
81
77
|
"""
|
82
78
|
pkey, app_name = self._get_runtime_env()
|
83
79
|
path = f"/appmesh/app/{app_name}/task"
|
80
|
+
query_params = {"process_key": pkey}
|
84
81
|
|
85
82
|
while True:
|
86
83
|
resp = self._client._request_http(
|
87
84
|
AppMeshClient.Method.GET,
|
88
85
|
path=path,
|
89
|
-
query=
|
86
|
+
query=query_params,
|
90
87
|
)
|
91
88
|
|
92
|
-
if resp.status_code
|
93
|
-
|
94
|
-
time.sleep(0.1)
|
95
|
-
continue
|
89
|
+
if resp.status_code == HTTPStatus.OK:
|
90
|
+
return resp.content
|
96
91
|
|
97
|
-
|
92
|
+
self._logger.warning("task_fetch failed with status %d: %s, retrying...", resp.status_code, resp.text)
|
93
|
+
time.sleep(self._RETRY_DELAY_SECONDS)
|
98
94
|
|
99
95
|
def task_return(self, result: Union[str, bytes]) -> None:
|
100
96
|
"""Return the result of a server-side invocation back to the original client.
|
101
97
|
|
102
|
-
Used by App Mesh application process to
|
103
|
-
after
|
98
|
+
Used by App Mesh application process to post the `result` to App Mesh service
|
99
|
+
after processing payload data so the invoking client can retrieve it.
|
104
100
|
|
105
101
|
Args:
|
106
|
-
result
|
102
|
+
result: Result payload to be delivered back to the client.
|
107
103
|
"""
|
108
104
|
pkey, app_name = self._get_runtime_env()
|
109
105
|
path = f"/appmesh/app/{app_name}/task"
|
106
|
+
query_params = {"process_key": pkey}
|
110
107
|
|
111
108
|
resp = self._client._request_http(
|
112
109
|
AppMeshClient.Method.PUT,
|
113
110
|
path=path,
|
114
|
-
query=
|
111
|
+
query=query_params,
|
115
112
|
body=result,
|
116
113
|
)
|
117
114
|
|
appmesh/server_tcp.py
CHANGED
@@ -3,8 +3,7 @@
|
|
3
3
|
|
4
4
|
# Standard library imports
|
5
5
|
import logging
|
6
|
-
import
|
7
|
-
from typing import Optional, Tuple
|
6
|
+
from typing import Optional, Tuple, Union
|
8
7
|
|
9
8
|
# Local imports
|
10
9
|
from .client_http import AppMeshClient
|
@@ -15,14 +14,12 @@ logger = logging.getLogger(__name__)
|
|
15
14
|
|
16
15
|
|
17
16
|
class AppMeshServerTCP(AppMeshServer):
|
18
|
-
"""
|
19
|
-
Server SDK for interacting with the local App Mesh service over TCP (TLS).
|
20
|
-
"""
|
17
|
+
"""Server SDK for interacting with the local App Mesh service over TCP (TLS)."""
|
21
18
|
|
22
19
|
def __init__(
|
23
20
|
self,
|
24
|
-
rest_ssl_verify=
|
25
|
-
rest_ssl_client_cert=None,
|
21
|
+
rest_ssl_verify: Union[bool, str] = AppMeshClient.DEFAULT_SSL_CA_CERT_PATH,
|
22
|
+
rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = None,
|
26
23
|
tcp_address: Tuple[str, int] = ("127.0.0.1", 6059),
|
27
24
|
*,
|
28
25
|
logger_: Optional[logging.Logger] = None,
|
@@ -34,6 +31,5 @@ class AppMeshServerTCP(AppMeshServer):
|
|
34
31
|
"""
|
35
32
|
# Deliberately avoid calling super().__init__ to inject a TCP client while keeping the same public API.
|
36
33
|
object.__init__(self)
|
37
|
-
# super().__init__(rest_ssl_verify=rest_ssl_verify, rest_ssl_client_cert=rest_ssl_client_cert)
|
38
34
|
self._client = AppMeshClientTCP(rest_ssl_verify=rest_ssl_verify, rest_ssl_client_cert=rest_ssl_client_cert, tcp_address=tcp_address)
|
39
35
|
self._logger = logger_ or logger
|
appmesh/tcp_transport.py
CHANGED
@@ -29,12 +29,10 @@ class TCPTransport:
|
|
29
29
|
|
30
30
|
Args:
|
31
31
|
address: Server address as (host, port) tuple.
|
32
|
-
|
33
32
|
ssl_verify: SSL server verification mode:
|
34
33
|
- True: Use system CA certificates
|
35
34
|
- False: Disable verification (insecure)
|
36
35
|
- str: Path to custom CA bundle or directory
|
37
|
-
|
38
36
|
ssl_client_cert: SSL client certificate:
|
39
37
|
- str: Path to PEM file with cert and key
|
40
38
|
- tuple: (cert_path, key_path)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
appmesh/__init__.py,sha256=on6GmTTwo6mrRbw_XUuf8sZdl5zBYsGT4CSf8l__NnI,2488
|
2
|
+
appmesh/app.py,sha256=bJyv_uOSMj5esMgkFWuvpMPS5ayFTBJLVll2Tvw8xB4,11213
|
3
|
+
appmesh/app_output.py,sha256=s6eqevFxETTVXSPTJX6JyGNpHHILv4ZyM7YWvlkuqQs,741
|
4
|
+
appmesh/app_run.py,sha256=m3ihaacx84o1rl2Oc3EbnppW8D-PTxdehftbWRe8rPk,1732
|
5
|
+
appmesh/appmesh_client.py,sha256=ywB2222PtJUffdfdxZcBfdhZs1KYyc7JvzMxwuK2qyI,378
|
6
|
+
appmesh/client_http.py,sha256=BsFgafrk4iUJlmrz21hlovyN5XAPYTGKjLNMt6aH8nM,58986
|
7
|
+
appmesh/client_http_oauth.py,sha256=zES-f6AnG6dUo754zd-HM6Pvu4vp_64vTKT61npY6HE,5934
|
8
|
+
appmesh/client_tcp.py,sha256=K3IneOSl8xiOIC9z4jHK_4Mhq2n8eeUDASvXJQiRjfQ,11817
|
9
|
+
appmesh/server_http.py,sha256=g2NYREF-f-vnSFVntAJDc0H43v_kNEqS7M4y7Q_DpEY,4193
|
10
|
+
appmesh/server_tcp.py,sha256=GXEVDmYwM19ZUKkRXm8eEzBep9rE1vzV-PwaTlROpFU,1364
|
11
|
+
appmesh/tcp_messages.py,sha256=E0cKWUta7NjuLoTGk-Z9CvbdZyakwG7hO8st_07E5L4,1991
|
12
|
+
appmesh/tcp_transport.py,sha256=nkml2hqSkyeJrGDKGAYjWDgOnSyYMEImVT1PQ6jeudk,8163
|
13
|
+
appmesh-1.6.14.dist-info/METADATA,sha256=2LZhu1Rp50ERLUa9DxhS-zQNM5om3-aLwtlcB4zNrUw,11814
|
14
|
+
appmesh-1.6.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
15
|
+
appmesh-1.6.14.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
|
16
|
+
appmesh-1.6.14.dist-info/RECORD,,
|
appmesh-1.6.13.dist-info/RECORD
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
appmesh/__init__.py,sha256=on6GmTTwo6mrRbw_XUuf8sZdl5zBYsGT4CSf8l__NnI,2488
|
2
|
-
appmesh/app.py,sha256=5kzQCvGH3LRRUTHsI2_MKEPD3M2qs8kvTxLhbNUqit4,10601
|
3
|
-
appmesh/app_output.py,sha256=s6eqevFxETTVXSPTJX6JyGNpHHILv4ZyM7YWvlkuqQs,741
|
4
|
-
appmesh/app_run.py,sha256=m3ihaacx84o1rl2Oc3EbnppW8D-PTxdehftbWRe8rPk,1732
|
5
|
-
appmesh/appmesh_client.py,sha256=ywB2222PtJUffdfdxZcBfdhZs1KYyc7JvzMxwuK2qyI,378
|
6
|
-
appmesh/client_http.py,sha256=FXCldfTZVU_5RSuSknlH1K2UiC6gOeotj-4NDQzdEhY,60752
|
7
|
-
appmesh/client_http_oauth.py,sha256=1d51o0JX_xtB8d2bEuM7_XJHcwMnhcjkbIq7GE1Zxm8,6120
|
8
|
-
appmesh/client_tcp.py,sha256=RX3T3OG4iOAju8ZPOnTjI_y97Y23_kWoNDjmKjtrBeU,11524
|
9
|
-
appmesh/server_http.py,sha256=wfyiIa2zC-uJR2ZNTGMjYWheqAfSRl0aAv5b_GYcwpE,4343
|
10
|
-
appmesh/server_tcp.py,sha256=J65kmN7DJftyW1LlF--S3keQ6VGmqXb778E79I1R0_k,1488
|
11
|
-
appmesh/tcp_messages.py,sha256=E0cKWUta7NjuLoTGk-Z9CvbdZyakwG7hO8st_07E5L4,1991
|
12
|
-
appmesh/tcp_transport.py,sha256=oUpcBXOaJhG5Hs6yqt9UG8_eENu0cEdNZIyC87LqI7Q,8165
|
13
|
-
appmesh-1.6.13.dist-info/METADATA,sha256=BJJB6M94Sz2yytdr9WRxCf56q1-LCzQ-2zQAfP5_1eU,11814
|
14
|
-
appmesh-1.6.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
15
|
-
appmesh-1.6.13.dist-info/top_level.txt,sha256=-y0MNQOGJxUzLdHZ6E_Rfv5_LNCkV-GTmOBME_b6pg8,8
|
16
|
-
appmesh-1.6.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|