appmesh 1.6.15__tar.gz → 1.6.16__tar.gz
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-1.6.15 → appmesh-1.6.16}/PKG-INFO +1 -1
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh/client_http.py +98 -96
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh/client_http_oauth.py +5 -7
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh/client_tcp.py +25 -25
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh/server_http.py +3 -3
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh/server_tcp.py +1 -1
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh/tcp_messages.py +1 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh/tcp_transport.py +1 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh.egg-info/PKG-INFO +1 -1
- {appmesh-1.6.15 → appmesh-1.6.16}/setup.py +1 -2
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh/__init__.py +0 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh/app.py +0 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh/app_output.py +0 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh/app_run.py +0 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh/appmesh_client.py +0 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh.egg-info/SOURCES.txt +0 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh.egg-info/dependency_links.txt +0 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh.egg-info/requires.txt +0 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/appmesh.egg-info/top_level.txt +0 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/pyproject.toml +0 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/setup.cfg +0 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/test/test_appmesh_client.py +0 -0
- {appmesh-1.6.15 → appmesh-1.6.16}/test/test_oauth2.py +0 -0
@@ -115,35 +115,35 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
115
115
|
"""
|
116
116
|
|
117
117
|
# Duration constants
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
118
|
+
_DURATION_ONE_WEEK_ISO = "P1W"
|
119
|
+
_DURATION_TWO_DAYS_ISO = "P2D"
|
120
|
+
_DURATION_TWO_DAYS_HALF_ISO = "P2DT12H"
|
121
|
+
_TOKEN_REFRESH_INTERVAL = 300 # 5 min to refresh token
|
122
|
+
_TOKEN_REFRESH_OFFSET = 30 # 30s before token expire to refresh token
|
123
123
|
|
124
124
|
# Platform-aware default SSL paths
|
125
125
|
_DEFAULT_SSL_DIR = Path("c:/local/appmesh/ssl" if os.name == "nt" else "/opt/appmesh/ssl")
|
126
|
-
|
127
|
-
|
128
|
-
|
126
|
+
_DEFAULT_SSL_CA_CERT_PATH = str(_DEFAULT_SSL_DIR / "ca.pem")
|
127
|
+
_DEFAULT_SSL_CLIENT_CERT_PATH = str(_DEFAULT_SSL_DIR / "client.pem")
|
128
|
+
_DEFAULT_SSL_CLIENT_KEY_PATH = str(_DEFAULT_SSL_DIR / "client-key.pem")
|
129
129
|
|
130
130
|
# JWT constants
|
131
|
-
|
131
|
+
_DEFAULT_JWT_AUDIENCE = "appmesh-service"
|
132
132
|
|
133
133
|
# HTTP headers and constants
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
134
|
+
_JSON_KEY_MESSAGE = "message"
|
135
|
+
_HTTP_USER_AGENT = "appmesh/python"
|
136
|
+
_HTTP_HEADER_KEY_AUTH = "Authorization"
|
137
|
+
_HTTP_HEADER_KEY_USER_AGENT = "User-Agent"
|
138
|
+
_HTTP_HEADER_KEY_X_TARGET_HOST = "X-Target-Host"
|
139
|
+
_HTTP_HEADER_KEY_X_FILE_PATH = "X-File-Path"
|
140
|
+
_HTTP_HEADER_JWT_SET_COOKIE = "X-Set-Cookie"
|
141
|
+
_HTTP_HEADER_NAME_CSRF_TOKEN = "X-CSRF-Token"
|
142
|
+
_COOKIE_TOKEN = "appmesh_auth_token"
|
143
|
+
_COOKIE_CSRF_TOKEN = "appmesh_csrf_token"
|
144
144
|
|
145
145
|
@unique
|
146
|
-
class
|
146
|
+
class _Method(Enum):
|
147
147
|
"""REST methods"""
|
148
148
|
|
149
149
|
GET = "GET"
|
@@ -152,7 +152,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
152
152
|
DELETE = "DELETE"
|
153
153
|
POST_STREAM = "POST_STREAM"
|
154
154
|
|
155
|
-
class
|
155
|
+
class _EncodingResponse(requests.Response):
|
156
156
|
"""Response subclass that handles encoding conversion on Windows."""
|
157
157
|
|
158
158
|
def __init__(self, response: requests.Response):
|
@@ -195,8 +195,8 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
195
195
|
def __init__(
|
196
196
|
self,
|
197
197
|
rest_url: str = "https://127.0.0.1:6060",
|
198
|
-
rest_ssl_verify: Union[bool, str] =
|
199
|
-
rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = (
|
198
|
+
rest_ssl_verify: Union[bool, str] = _DEFAULT_SSL_CA_CERT_PATH,
|
199
|
+
rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = (_DEFAULT_SSL_CLIENT_CERT_PATH, _DEFAULT_SSL_CLIENT_KEY_PATH),
|
200
200
|
rest_timeout: Tuple[float, float] = (60, 300),
|
201
201
|
jwt_token: Optional[str] = None,
|
202
202
|
rest_cookie_file: Optional[str] = None,
|
@@ -235,7 +235,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
235
235
|
self.session = requests.Session()
|
236
236
|
self.cookie_file = rest_cookie_file
|
237
237
|
loaded = self._load_cookies(rest_cookie_file)
|
238
|
-
cookie_token = self._get_cookie_value(self.session.cookies, self.
|
238
|
+
cookie_token = self._get_cookie_value(self.session.cookies, self._COOKIE_TOKEN) if loaded else None
|
239
239
|
|
240
240
|
# Set property last after all dependencies are initialized to setup refresh timer
|
241
241
|
self.jwt_token = jwt_token or cookie_token
|
@@ -246,6 +246,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
246
246
|
if not logging.root.handlers:
|
247
247
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
|
248
248
|
|
249
|
+
# @abc.abstractmethod
|
249
250
|
def _get_access_token(self) -> str:
|
250
251
|
"""Get the current access token."""
|
251
252
|
return self.jwt_token or ""
|
@@ -305,7 +306,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
305
306
|
expiry = decoded_token.get("exp", 0)
|
306
307
|
current_time = time.time()
|
307
308
|
time_to_expiry = expiry - current_time
|
308
|
-
needs_refresh = time_to_expiry < self.
|
309
|
+
needs_refresh = time_to_expiry < self._TOKEN_REFRESH_OFFSET
|
309
310
|
|
310
311
|
# Refresh token if needed
|
311
312
|
if needs_refresh:
|
@@ -328,15 +329,15 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
328
329
|
|
329
330
|
try:
|
330
331
|
# Default to checking after 60 seconds
|
331
|
-
check_interval = self.
|
332
|
+
check_interval = self._TOKEN_REFRESH_INTERVAL
|
332
333
|
|
333
334
|
# Calculate more precise check time if expiry is known
|
334
335
|
if time_to_expiry is not None:
|
335
|
-
if time_to_expiry <= self.
|
336
|
+
if time_to_expiry <= self._TOKEN_REFRESH_OFFSET: # Expires within 5 minutes
|
336
337
|
check_interval = 1 # Almost immediate refresh
|
337
338
|
else:
|
338
339
|
# Check at earlier of 5 minutes before expiry or regular interval
|
339
|
-
check_interval = max(1, min(time_to_expiry - self.
|
340
|
+
check_interval = max(1, min(time_to_expiry - self._TOKEN_REFRESH_OFFSET, self._TOKEN_REFRESH_INTERVAL))
|
340
341
|
|
341
342
|
# Create timer to execute refresh check
|
342
343
|
self._token_refresh_timer = threading.Timer(check_interval, self._check_and_refresh_token)
|
@@ -346,6 +347,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
346
347
|
except Exception as e:
|
347
348
|
logging.error("Auto-refresh: Failed to schedule token refresh: %s", e)
|
348
349
|
|
350
|
+
# @abc.abstractmethod
|
349
351
|
def close(self):
|
350
352
|
"""Close the session and release resources."""
|
351
353
|
# Cancel token refresh timer
|
@@ -434,7 +436,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
434
436
|
user_name: str,
|
435
437
|
user_pwd: str,
|
436
438
|
totp_code: Optional[str] = "",
|
437
|
-
timeout_seconds: Union[str, int] =
|
439
|
+
timeout_seconds: Union[str, int] = _DURATION_ONE_WEEK_ISO,
|
438
440
|
audience: Optional[str] = None,
|
439
441
|
) -> str:
|
440
442
|
"""Login with user name and password.
|
@@ -454,18 +456,18 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
454
456
|
|
455
457
|
credentials = f"{user_name}:{user_pwd}".encode()
|
456
458
|
headers = {
|
457
|
-
self.
|
459
|
+
self._HTTP_HEADER_KEY_AUTH: f"Basic {base64.b64encode(credentials).decode()}",
|
458
460
|
"X-Expire-Seconds": str(self._parse_duration(timeout_seconds)),
|
459
461
|
}
|
460
462
|
if audience:
|
461
463
|
headers["X-Audience"] = audience
|
462
464
|
if self.cookie_file:
|
463
|
-
headers[self.
|
465
|
+
headers[self._HTTP_HEADER_JWT_SET_COOKIE] = "true"
|
464
466
|
# if totp_code:
|
465
467
|
# headers["X-Totp-Code"] = totp_code
|
466
468
|
|
467
469
|
resp = self._request_http(
|
468
|
-
AppMeshClient.
|
470
|
+
AppMeshClient._Method.POST,
|
469
471
|
path="/appmesh/login",
|
470
472
|
header=headers,
|
471
473
|
)
|
@@ -484,14 +486,14 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
484
486
|
|
485
487
|
return self.jwt_token
|
486
488
|
|
487
|
-
def validate_totp(self, username: str, challenge: str, code: str, timeout: Union[int, str] =
|
489
|
+
def validate_totp(self, username: str, challenge: str, code: str, timeout: Union[int, str] = _DURATION_ONE_WEEK_ISO) -> str:
|
488
490
|
"""Validate TOTP challenge and obtain a new JWT token.
|
489
491
|
|
490
492
|
Args:
|
491
493
|
username: Username to validate.
|
492
494
|
challenge: Challenge string from server.
|
493
495
|
code: TOTP code to validate.
|
494
|
-
timeout: Token expiration duration, defaults to `
|
496
|
+
timeout: Token expiration duration, defaults to `_DURATION_ONE_WEEK_ISO` (1 week).
|
495
497
|
Accepts either:
|
496
498
|
- **ISO 8601 duration string** (e.g., `'P1Y2M3DT4H5M6S'`, `'P1W'`)
|
497
499
|
- **Numeric value (seconds)** for simpler cases.
|
@@ -506,10 +508,10 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
506
508
|
"expire_seconds": self._parse_duration(timeout),
|
507
509
|
}
|
508
510
|
|
509
|
-
headers = {self.
|
511
|
+
headers = {self._HTTP_HEADER_JWT_SET_COOKIE: "true"} if self.cookie_file else {}
|
510
512
|
|
511
513
|
resp = self._request_http(
|
512
|
-
AppMeshClient.
|
514
|
+
AppMeshClient._Method.POST,
|
513
515
|
path="/appmesh/totp/validate",
|
514
516
|
body=body,
|
515
517
|
header=headers,
|
@@ -525,7 +527,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
525
527
|
if not self.jwt_token or not isinstance(self.jwt_token, str):
|
526
528
|
return False
|
527
529
|
|
528
|
-
resp = self._request_http(AppMeshClient.
|
530
|
+
resp = self._request_http(AppMeshClient._Method.POST, path="/appmesh/self/logoff")
|
529
531
|
self.jwt_token = None
|
530
532
|
return resp.status_code == HTTPStatus.OK
|
531
533
|
|
@@ -556,7 +558,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
556
558
|
if permission:
|
557
559
|
headers["X-Permission"] = permission
|
558
560
|
|
559
|
-
resp = self._request_http(AppMeshClient.
|
561
|
+
resp = self._request_http(AppMeshClient._Method.POST, path="/appmesh/auth", header=headers)
|
560
562
|
|
561
563
|
if resp.status_code != HTTPStatus.OK:
|
562
564
|
self.jwt_token = old_token
|
@@ -564,7 +566,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
564
566
|
|
565
567
|
return True
|
566
568
|
|
567
|
-
def renew_token(self, timeout: Union[int, str] =
|
569
|
+
def renew_token(self, timeout: Union[int, str] = _DURATION_ONE_WEEK_ISO) -> str:
|
568
570
|
"""Renew the current token.
|
569
571
|
|
570
572
|
Args:
|
@@ -580,7 +582,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
580
582
|
raise Exception("Unsupported token format")
|
581
583
|
|
582
584
|
resp = self._request_http(
|
583
|
-
AppMeshClient.
|
585
|
+
AppMeshClient._Method.POST,
|
584
586
|
path="/appmesh/token/renew",
|
585
587
|
header={"X-Expire-Seconds": str(self._parse_duration(timeout))},
|
586
588
|
)
|
@@ -597,7 +599,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
597
599
|
|
598
600
|
def get_totp_secret(self) -> str:
|
599
601
|
"""Generate TOTP secret for the current user."""
|
600
|
-
resp = self._request_http(method=AppMeshClient.
|
602
|
+
resp = self._request_http(method=AppMeshClient._Method.POST, path="/appmesh/totp/secret")
|
601
603
|
|
602
604
|
if resp.status_code != HTTPStatus.OK:
|
603
605
|
raise Exception(resp.text)
|
@@ -619,7 +621,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
619
621
|
The new JWT token if setup succeeds.
|
620
622
|
"""
|
621
623
|
resp = self._request_http(
|
622
|
-
method=AppMeshClient.
|
624
|
+
method=AppMeshClient._Method.POST,
|
623
625
|
path="/appmesh/totp/setup",
|
624
626
|
header={"X-Totp-Code": totp_code},
|
625
627
|
)
|
@@ -634,7 +636,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
634
636
|
def disable_totp(self, user: str = "self") -> None:
|
635
637
|
"""Disable 2FA for the specified user."""
|
636
638
|
resp = self._request_http(
|
637
|
-
method=AppMeshClient.
|
639
|
+
method=AppMeshClient._Method.POST,
|
638
640
|
path=f"/appmesh/totp/{user}/disable",
|
639
641
|
)
|
640
642
|
|
@@ -662,7 +664,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
662
664
|
########################################
|
663
665
|
def view_app(self, app_name: str) -> App:
|
664
666
|
"""Get information about a specific application."""
|
665
|
-
resp = self._request_http(AppMeshClient.
|
667
|
+
resp = self._request_http(AppMeshClient._Method.GET, path=f"/appmesh/app/{app_name}")
|
666
668
|
|
667
669
|
if resp.status_code != HTTPStatus.OK:
|
668
670
|
raise Exception(resp.text)
|
@@ -671,7 +673,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
671
673
|
|
672
674
|
def view_all_apps(self) -> list:
|
673
675
|
"""Get information about all applications."""
|
674
|
-
resp = self._request_http(AppMeshClient.
|
676
|
+
resp = self._request_http(AppMeshClient._Method.GET, path="/appmesh/applications")
|
675
677
|
|
676
678
|
if resp.status_code != HTTPStatus.OK:
|
677
679
|
raise Exception(resp.text)
|
@@ -694,7 +696,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
694
696
|
AppOutput object.
|
695
697
|
"""
|
696
698
|
resp = self._request_http(
|
697
|
-
AppMeshClient.
|
699
|
+
AppMeshClient._Method.GET,
|
698
700
|
path=f"/appmesh/app/{app_name}/output",
|
699
701
|
query={
|
700
702
|
"stdout_position": str(stdout_position),
|
@@ -712,7 +714,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
712
714
|
|
713
715
|
def check_app_health(self, app_name: str) -> bool:
|
714
716
|
"""Check the health status of an application."""
|
715
|
-
resp = self._request_http(AppMeshClient.
|
717
|
+
resp = self._request_http(AppMeshClient._Method.GET, path=f"/appmesh/app/{app_name}/health")
|
716
718
|
|
717
719
|
if resp.status_code != HTTPStatus.OK:
|
718
720
|
raise Exception(resp.text)
|
@@ -725,7 +727,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
725
727
|
def add_app(self, app: App) -> App:
|
726
728
|
# type: (App) -> App
|
727
729
|
"""Register a new application."""
|
728
|
-
resp = self._request_http(AppMeshClient.
|
730
|
+
resp = self._request_http(AppMeshClient._Method.PUT, path=f"/appmesh/app/{app.name}", body=app.json())
|
729
731
|
|
730
732
|
if resp.status_code != HTTPStatus.OK:
|
731
733
|
raise Exception(resp.text)
|
@@ -734,7 +736,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
734
736
|
|
735
737
|
def delete_app(self, app_name: str) -> bool:
|
736
738
|
"""Remove an application."""
|
737
|
-
resp = self._request_http(AppMeshClient.
|
739
|
+
resp = self._request_http(AppMeshClient._Method.DELETE, path=f"/appmesh/app/{app_name}")
|
738
740
|
|
739
741
|
if resp.status_code == HTTPStatus.OK:
|
740
742
|
return True
|
@@ -745,14 +747,14 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
745
747
|
|
746
748
|
def enable_app(self, app_name: str) -> None:
|
747
749
|
"""Enable an application."""
|
748
|
-
resp = self._request_http(AppMeshClient.
|
750
|
+
resp = self._request_http(AppMeshClient._Method.POST, path=f"/appmesh/app/{app_name}/enable")
|
749
751
|
|
750
752
|
if resp.status_code != HTTPStatus.OK:
|
751
753
|
raise Exception(resp.text)
|
752
754
|
|
753
755
|
def disable_app(self, app_name: str) -> None:
|
754
756
|
"""Disable an application."""
|
755
|
-
resp = self._request_http(AppMeshClient.
|
757
|
+
resp = self._request_http(AppMeshClient._Method.POST, path=f"/appmesh/app/{app_name}/disable")
|
756
758
|
|
757
759
|
if resp.status_code != HTTPStatus.OK:
|
758
760
|
raise Exception(resp.text)
|
@@ -762,7 +764,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
762
764
|
########################################
|
763
765
|
def view_host_resources(self) -> dict:
|
764
766
|
"""Get a report of host resources including CPU, memory, and disk."""
|
765
|
-
resp = self._request_http(AppMeshClient.
|
767
|
+
resp = self._request_http(AppMeshClient._Method.GET, path="/appmesh/resources")
|
766
768
|
|
767
769
|
if resp.status_code != HTTPStatus.OK:
|
768
770
|
raise Exception(resp.text)
|
@@ -771,7 +773,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
771
773
|
|
772
774
|
def view_config(self) -> dict:
|
773
775
|
"""Get the App Mesh configuration in JSON format."""
|
774
|
-
resp = self._request_http(AppMeshClient.
|
776
|
+
resp = self._request_http(AppMeshClient._Method.GET, path="/appmesh/config")
|
775
777
|
|
776
778
|
if resp.status_code != HTTPStatus.OK:
|
777
779
|
raise Exception(resp.text)
|
@@ -780,7 +782,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
780
782
|
|
781
783
|
def set_config(self, config_json: dict) -> dict:
|
782
784
|
"""Update the configuration."""
|
783
|
-
resp = self._request_http(AppMeshClient.
|
785
|
+
resp = self._request_http(AppMeshClient._Method.POST, path="/appmesh/config", body=config_json)
|
784
786
|
|
785
787
|
if resp.status_code != HTTPStatus.OK:
|
786
788
|
raise Exception(resp.text)
|
@@ -789,7 +791,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
789
791
|
|
790
792
|
def set_log_level(self, level: str = "DEBUG") -> str:
|
791
793
|
"""Update the log level."""
|
792
|
-
resp = self._request_http(AppMeshClient.
|
794
|
+
resp = self._request_http(AppMeshClient._Method.POST, path="/appmesh/config", body={"BaseConfig": {"LogLevel": level}})
|
793
795
|
|
794
796
|
if resp.status_code != HTTPStatus.OK:
|
795
797
|
raise Exception(resp.text)
|
@@ -807,7 +809,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
807
809
|
}
|
808
810
|
|
809
811
|
resp = self._request_http(
|
810
|
-
method=AppMeshClient.
|
812
|
+
method=AppMeshClient._Method.POST,
|
811
813
|
path=f"/appmesh/user/{user_name}/passwd",
|
812
814
|
body=body,
|
813
815
|
)
|
@@ -818,7 +820,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
818
820
|
def add_user(self, user_name: str, user_json: dict) -> None:
|
819
821
|
"""Add a new user."""
|
820
822
|
resp = self._request_http(
|
821
|
-
method=AppMeshClient.
|
823
|
+
method=AppMeshClient._Method.PUT,
|
822
824
|
path=f"/appmesh/user/{user_name}",
|
823
825
|
body=user_json,
|
824
826
|
)
|
@@ -828,7 +830,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
828
830
|
def delete_user(self, user_name: str):
|
829
831
|
"""Delete a user."""
|
830
832
|
resp = self._request_http(
|
831
|
-
method=AppMeshClient.
|
833
|
+
method=AppMeshClient._Method.DELETE,
|
832
834
|
path=f"/appmesh/user/{user_name}",
|
833
835
|
)
|
834
836
|
if resp.status_code != HTTPStatus.OK:
|
@@ -837,7 +839,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
837
839
|
def lock_user(self, user_name: str) -> None:
|
838
840
|
"""Lock a user."""
|
839
841
|
resp = self._request_http(
|
840
|
-
method=AppMeshClient.
|
842
|
+
method=AppMeshClient._Method.POST,
|
841
843
|
path=f"/appmesh/user/{user_name}/lock",
|
842
844
|
)
|
843
845
|
|
@@ -847,7 +849,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
847
849
|
def unlock_user(self, user_name: str) -> None:
|
848
850
|
"""Unlock a user."""
|
849
851
|
resp = self._request_http(
|
850
|
-
method=AppMeshClient.
|
852
|
+
method=AppMeshClient._Method.POST,
|
851
853
|
path=f"/appmesh/user/{user_name}/unlock",
|
852
854
|
)
|
853
855
|
|
@@ -856,7 +858,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
856
858
|
|
857
859
|
def view_users(self) -> dict:
|
858
860
|
"""Get information about all users."""
|
859
|
-
resp = self._request_http(method=AppMeshClient.
|
861
|
+
resp = self._request_http(method=AppMeshClient._Method.GET, path="/appmesh/users")
|
860
862
|
|
861
863
|
if resp.status_code != HTTPStatus.OK:
|
862
864
|
raise Exception(resp.text)
|
@@ -865,7 +867,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
865
867
|
|
866
868
|
def view_self(self) -> dict:
|
867
869
|
"""Get information about the current user."""
|
868
|
-
resp = self._request_http(method=AppMeshClient.
|
870
|
+
resp = self._request_http(method=AppMeshClient._Method.GET, path="/appmesh/user/self")
|
869
871
|
|
870
872
|
if resp.status_code != HTTPStatus.OK:
|
871
873
|
raise Exception(resp.text)
|
@@ -874,7 +876,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
874
876
|
|
875
877
|
def view_groups(self) -> list:
|
876
878
|
"""Get information about all user groups."""
|
877
|
-
resp = self._request_http(method=AppMeshClient.
|
879
|
+
resp = self._request_http(method=AppMeshClient._Method.GET, path="/appmesh/user/groups")
|
878
880
|
|
879
881
|
if resp.status_code != HTTPStatus.OK:
|
880
882
|
raise Exception(resp.text)
|
@@ -883,7 +885,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
883
885
|
|
884
886
|
def view_permissions(self) -> list:
|
885
887
|
"""Get information about all available permissions."""
|
886
|
-
resp = self._request_http(method=AppMeshClient.
|
888
|
+
resp = self._request_http(method=AppMeshClient._Method.GET, path="/appmesh/permissions")
|
887
889
|
|
888
890
|
if resp.status_code != HTTPStatus.OK:
|
889
891
|
raise Exception(resp.text)
|
@@ -892,7 +894,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
892
894
|
|
893
895
|
def view_user_permissions(self) -> list:
|
894
896
|
"""Get information about the permissions of the current user."""
|
895
|
-
resp = self._request_http(method=AppMeshClient.
|
897
|
+
resp = self._request_http(method=AppMeshClient._Method.GET, path="/appmesh/user/permissions")
|
896
898
|
|
897
899
|
if resp.status_code != HTTPStatus.OK:
|
898
900
|
raise Exception(resp.text)
|
@@ -901,7 +903,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
901
903
|
|
902
904
|
def view_roles(self) -> list:
|
903
905
|
"""Get information about all roles with permission definitions."""
|
904
|
-
resp = self._request_http(method=AppMeshClient.
|
906
|
+
resp = self._request_http(method=AppMeshClient._Method.GET, path="/appmesh/roles")
|
905
907
|
|
906
908
|
if resp.status_code != HTTPStatus.OK:
|
907
909
|
raise Exception(resp.text)
|
@@ -910,7 +912,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
910
912
|
|
911
913
|
def update_role(self, role_name: str, role_permission_json: dict) -> None:
|
912
914
|
"""Update or add a role with defined permissions."""
|
913
|
-
resp = self._request_http(method=AppMeshClient.
|
915
|
+
resp = self._request_http(method=AppMeshClient._Method.POST, path=f"/appmesh/role/{role_name}", body=role_permission_json)
|
914
916
|
|
915
917
|
if resp.status_code != HTTPStatus.OK:
|
916
918
|
raise Exception(resp.text)
|
@@ -918,7 +920,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
918
920
|
def delete_role(self, role_name: str) -> None:
|
919
921
|
"""Delete a user role."""
|
920
922
|
resp = self._request_http(
|
921
|
-
method=AppMeshClient.
|
923
|
+
method=AppMeshClient._Method.DELETE,
|
922
924
|
path=f"/appmesh/role/{role_name}",
|
923
925
|
)
|
924
926
|
|
@@ -931,7 +933,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
931
933
|
def add_tag(self, tag_name: str, tag_value: str) -> None:
|
932
934
|
"""Add a new label."""
|
933
935
|
resp = self._request_http(
|
934
|
-
AppMeshClient.
|
936
|
+
AppMeshClient._Method.PUT,
|
935
937
|
query={"value": tag_value},
|
936
938
|
path=f"/appmesh/label/{tag_name}",
|
937
939
|
)
|
@@ -941,14 +943,14 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
941
943
|
|
942
944
|
def delete_tag(self, tag_name: str) -> None:
|
943
945
|
"""Delete a label."""
|
944
|
-
resp = self._request_http(AppMeshClient.
|
946
|
+
resp = self._request_http(AppMeshClient._Method.DELETE, path=f"/appmesh/label/{tag_name}")
|
945
947
|
|
946
948
|
if resp.status_code != HTTPStatus.OK:
|
947
949
|
raise Exception(resp.text)
|
948
950
|
|
949
951
|
def view_tags(self) -> dict:
|
950
952
|
"""Get information about all labels."""
|
951
|
-
resp = self._request_http(AppMeshClient.
|
953
|
+
resp = self._request_http(AppMeshClient._Method.GET, path="/appmesh/labels")
|
952
954
|
|
953
955
|
if resp.status_code != HTTPStatus.OK:
|
954
956
|
raise Exception(resp.text)
|
@@ -960,7 +962,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
960
962
|
########################################
|
961
963
|
def get_metrics(self) -> str:
|
962
964
|
"""Get Prometheus metrics."""
|
963
|
-
resp = self._request_http(AppMeshClient.
|
965
|
+
resp = self._request_http(AppMeshClient._Method.GET, path="/appmesh/metrics")
|
964
966
|
|
965
967
|
if resp.status_code != HTTPStatus.OK:
|
966
968
|
raise Exception(resp.text)
|
@@ -972,7 +974,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
972
974
|
########################################
|
973
975
|
def download_file(self, remote_file: str, local_file: str, preserve_permissions: bool = True) -> None:
|
974
976
|
"""Download a remote file to the local system."""
|
975
|
-
resp = self._request_http(AppMeshClient.
|
977
|
+
resp = self._request_http(AppMeshClient._Method.GET, path="/appmesh/file/download", header={self._HTTP_HEADER_KEY_X_FILE_PATH: remote_file})
|
976
978
|
resp.raise_for_status()
|
977
979
|
|
978
980
|
# Write the file content locally
|
@@ -1004,19 +1006,19 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1004
1006
|
with local_path.open("rb") as fp:
|
1005
1007
|
encoder = MultipartEncoder(fields={"filename": os.path.basename(remote_file), "file": ("filename", fp, "application/octet-stream")})
|
1006
1008
|
|
1007
|
-
header = {self.
|
1009
|
+
header = {self._HTTP_HEADER_KEY_X_FILE_PATH: parse.quote(remote_file), "Content-Type": encoder.content_type}
|
1008
1010
|
|
1009
1011
|
# Include file attributes if requested
|
1010
1012
|
if preserve_permissions:
|
1011
1013
|
file_stat = local_path.stat()
|
1012
|
-
header["X-File-Mode"] = str(file_stat.st_mode & 0o777)
|
1014
|
+
header["X-File-Mode"] = str(file_stat.st_mode & 0o777) # Mask to keep only permission bits
|
1013
1015
|
header["X-File-User"] = str(file_stat.st_uid)
|
1014
1016
|
header["X-File-Group"] = str(file_stat.st_gid)
|
1015
1017
|
|
1016
1018
|
# Upload file with or without attributes
|
1017
1019
|
# https://stackoverflow.com/questions/22567306/python-requests-file-upload
|
1018
1020
|
resp = self._request_http(
|
1019
|
-
AppMeshClient.
|
1021
|
+
AppMeshClient._Method.POST_STREAM,
|
1020
1022
|
path="/appmesh/file/upload",
|
1021
1023
|
header=header,
|
1022
1024
|
body=encoder,
|
@@ -1050,7 +1052,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1050
1052
|
str: The HTTP response body returned by the remote application/service.
|
1051
1053
|
"""
|
1052
1054
|
resp = self._request_http(
|
1053
|
-
AppMeshClient.
|
1055
|
+
AppMeshClient._Method.POST,
|
1054
1056
|
path=f"/appmesh/app/{app_name}/task",
|
1055
1057
|
body=data,
|
1056
1058
|
query={"timeout": str(timeout)},
|
@@ -1071,7 +1073,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1071
1073
|
bool: Task exist and cancled status.
|
1072
1074
|
"""
|
1073
1075
|
resp = self._request_http(
|
1074
|
-
AppMeshClient.
|
1076
|
+
AppMeshClient._Method.DELETE,
|
1075
1077
|
path=f"/appmesh/app/{app_name}/task",
|
1076
1078
|
)
|
1077
1079
|
return resp.status_code == HTTPStatus.OK
|
@@ -1079,8 +1081,8 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1079
1081
|
def run_app_async(
|
1080
1082
|
self,
|
1081
1083
|
app: Union[App, str],
|
1082
|
-
max_time_seconds: Union[int, str] =
|
1083
|
-
life_cycle_seconds: Union[int, str] =
|
1084
|
+
max_time_seconds: Union[int, str] = _DURATION_TWO_DAYS_ISO,
|
1085
|
+
life_cycle_seconds: Union[int, str] = _DURATION_TWO_DAYS_HALF_ISO,
|
1084
1086
|
) -> AppRun:
|
1085
1087
|
"""Run an application asynchronously on a remote system without blocking the API.
|
1086
1088
|
|
@@ -1103,7 +1105,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1103
1105
|
app = App({"command": app, "shell": True})
|
1104
1106
|
|
1105
1107
|
resp = self._request_http(
|
1106
|
-
AppMeshClient.
|
1108
|
+
AppMeshClient._Method.POST,
|
1107
1109
|
body=app.json(),
|
1108
1110
|
path="/appmesh/app/run",
|
1109
1111
|
query={
|
@@ -1165,8 +1167,8 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1165
1167
|
self,
|
1166
1168
|
app: Union[App, str],
|
1167
1169
|
stdout_print: bool = True,
|
1168
|
-
max_time_seconds: Union[int, str] =
|
1169
|
-
life_cycle_seconds: Union[int, str] =
|
1170
|
+
max_time_seconds: Union[int, str] = _DURATION_TWO_DAYS_ISO,
|
1171
|
+
life_cycle_seconds: Union[int, str] = _DURATION_TWO_DAYS_HALF_ISO,
|
1170
1172
|
) -> Tuple[Union[int, None], str]:
|
1171
1173
|
"""Synchronously run an application remotely, blocking until completion, and return the result.
|
1172
1174
|
|
@@ -1190,7 +1192,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1190
1192
|
app = App({"command": app, "shell": True})
|
1191
1193
|
|
1192
1194
|
resp = self._request_http(
|
1193
|
-
AppMeshClient.
|
1195
|
+
AppMeshClient._Method.POST,
|
1194
1196
|
body=app.json(),
|
1195
1197
|
path="/appmesh/app/syncrun",
|
1196
1198
|
query={
|
@@ -1210,7 +1212,7 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1210
1212
|
|
1211
1213
|
return exit_code, resp.text
|
1212
1214
|
|
1213
|
-
def _request_http(self, method:
|
1215
|
+
def _request_http(self, method: _Method, path: str, query: Optional[dict] = None, header: Optional[dict] = None, body=None) -> requests.Response:
|
1214
1216
|
"""Make an HTTP request."""
|
1215
1217
|
rest_url = parse.urljoin(self.auth_server_url, path)
|
1216
1218
|
|
@@ -1219,23 +1221,23 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1219
1221
|
|
1220
1222
|
if self.cookie_file:
|
1221
1223
|
# Cookie-based token
|
1222
|
-
csrf_token = self._get_cookie_value(self.session.cookies, self.
|
1224
|
+
csrf_token = self._get_cookie_value(self.session.cookies, self._COOKIE_CSRF_TOKEN)
|
1223
1225
|
if csrf_token:
|
1224
|
-
headers[self.
|
1226
|
+
headers[self._HTTP_HEADER_NAME_CSRF_TOKEN] = csrf_token
|
1225
1227
|
else:
|
1226
1228
|
# Api-based token
|
1227
1229
|
access_token = self._get_access_token()
|
1228
1230
|
if access_token:
|
1229
|
-
headers[self.
|
1231
|
+
headers[self._HTTP_HEADER_KEY_AUTH] = f"Bearer {access_token}"
|
1230
1232
|
|
1231
1233
|
if self.forward_to:
|
1232
1234
|
target_host = self.forward_to
|
1233
1235
|
if ":" not in target_host:
|
1234
1236
|
port = parse.urlsplit(self.auth_server_url).port
|
1235
1237
|
target_host = f"{target_host}:{port}"
|
1236
|
-
headers[self.
|
1238
|
+
headers[self._HTTP_HEADER_KEY_X_TARGET_HOST] = target_host
|
1237
1239
|
|
1238
|
-
headers[self.
|
1240
|
+
headers[self._HTTP_HEADER_KEY_USER_AGENT] = self._HTTP_USER_AGENT
|
1239
1241
|
|
1240
1242
|
# Convert body to JSON string if it's a dict or list
|
1241
1243
|
if isinstance(body, (dict, list)):
|
@@ -1251,21 +1253,21 @@ class AppMeshClient(metaclass=abc.ABCMeta):
|
|
1251
1253
|
"timeout": self.rest_timeout,
|
1252
1254
|
}
|
1253
1255
|
|
1254
|
-
if method == AppMeshClient.
|
1256
|
+
if method == AppMeshClient._Method.GET:
|
1255
1257
|
resp = self.session.get(params=query, **request_kwargs)
|
1256
|
-
elif method == AppMeshClient.
|
1258
|
+
elif method == AppMeshClient._Method.POST:
|
1257
1259
|
resp = self.session.post(params=query, data=body, **request_kwargs)
|
1258
|
-
elif method == AppMeshClient.
|
1260
|
+
elif method == AppMeshClient._Method.POST_STREAM:
|
1259
1261
|
resp = self.session.post(params=query, data=body, stream=True, **request_kwargs)
|
1260
|
-
elif method == AppMeshClient.
|
1262
|
+
elif method == AppMeshClient._Method.DELETE:
|
1261
1263
|
resp = self.session.delete(**request_kwargs)
|
1262
|
-
elif method == AppMeshClient.
|
1264
|
+
elif method == AppMeshClient._Method.PUT:
|
1263
1265
|
resp = self.session.put(params=query, data=body, **request_kwargs)
|
1264
1266
|
else:
|
1265
1267
|
raise Exception("Invalid http method", method)
|
1266
1268
|
|
1267
1269
|
# Wrap the response for encoding handling
|
1268
|
-
return AppMeshClient.
|
1270
|
+
return AppMeshClient._EncodingResponse(resp)
|
1269
1271
|
|
1270
1272
|
except requests.exceptions.RequestException as e:
|
1271
1273
|
raise Exception(f"HTTP request failed: {e}") from e
|
@@ -1,8 +1,7 @@
|
|
1
1
|
# client_http_oauth.py
|
2
2
|
# pylint: disable=line-too-long,broad-exception-caught,too-many-lines, import-outside-toplevel, protected-access
|
3
|
-
import os
|
4
3
|
import logging
|
5
|
-
from typing import Optional, Union
|
4
|
+
from typing import Optional, Union, Tuple
|
6
5
|
from keycloak import KeycloakOpenID
|
7
6
|
from .client_http import AppMeshClient
|
8
7
|
|
@@ -18,10 +17,10 @@ class AppMeshClientOAuth(AppMeshClient):
|
|
18
17
|
self,
|
19
18
|
oauth2: dict, # Required for Keycloak
|
20
19
|
rest_url: str = "https://127.0.0.1:6060",
|
21
|
-
rest_ssl_verify=AppMeshClient.
|
22
|
-
rest_ssl_client_cert=
|
23
|
-
rest_timeout=(60, 300),
|
24
|
-
jwt_token=None, # Keycloak dict
|
20
|
+
rest_ssl_verify: Union[bool, str] = AppMeshClient._DEFAULT_SSL_CA_CERT_PATH,
|
21
|
+
rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = None,
|
22
|
+
rest_timeout: Tuple[float, float] = (60, 300),
|
23
|
+
jwt_token: Optional[dict] = None, # Keycloak dict
|
25
24
|
auto_refresh_token: bool = True, # Default to True for Keycloak
|
26
25
|
):
|
27
26
|
"""Initialize an App Mesh HTTP client with Keycloak support.
|
@@ -63,7 +62,6 @@ class AppMeshClientOAuth(AppMeshClient):
|
|
63
62
|
user_name: str,
|
64
63
|
user_pwd: str,
|
65
64
|
totp_code: Optional[str] = "",
|
66
|
-
timeout_seconds: Union[str, int] = AppMeshClient.DURATION_ONE_WEEK_ISO,
|
67
65
|
) -> dict:
|
68
66
|
"""Login with user name and password using Keycloak.
|
69
67
|
Args:
|
@@ -44,15 +44,15 @@ class AppMeshClientTCP(AppMeshClient):
|
|
44
44
|
"""
|
45
45
|
|
46
46
|
# TLS-optimized chunk size, leaves room for TLS overhead within the 16 KB limit
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
_TCP_BLOCK_SIZE = 16 * 1024 - 128
|
48
|
+
_ENCODING_UTF8 = "utf-8"
|
49
|
+
_HTTP_USER_AGENT_TCP = "appmesh/python/tcp"
|
50
|
+
_HTTP_HEADER_KEY_X_SEND_FILE_SOCKET = "X-Send-File-Socket"
|
51
|
+
_HTTP_HEADER_KEY_X_RECV_FILE_SOCKET = "X-Recv-File-Socket"
|
52
52
|
|
53
53
|
def __init__(
|
54
54
|
self,
|
55
|
-
rest_ssl_verify: Union[bool, str] = AppMeshClient.
|
55
|
+
rest_ssl_verify: Union[bool, str] = AppMeshClient._DEFAULT_SSL_CA_CERT_PATH,
|
56
56
|
rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = None,
|
57
57
|
jwt_token: Optional[str] = None,
|
58
58
|
tcp_address: Tuple[str, int] = ("127.0.0.1", 6059),
|
@@ -100,14 +100,14 @@ class AppMeshClientTCP(AppMeshClient):
|
|
100
100
|
return body
|
101
101
|
|
102
102
|
if isinstance(body, str):
|
103
|
-
return body.encode(self.
|
103
|
+
return body.encode(self._ENCODING_UTF8)
|
104
104
|
|
105
105
|
if isinstance(body, (dict, list)):
|
106
|
-
return json.dumps(body).encode(self.
|
106
|
+
return json.dumps(body).encode(self._ENCODING_UTF8)
|
107
107
|
|
108
108
|
raise TypeError(f"Unsupported body type: {type(body)}")
|
109
109
|
|
110
|
-
def _request_http(self, method: AppMeshClient.
|
110
|
+
def _request_http(self, method: AppMeshClient._Method, path: str, query: Optional[dict] = None, header: Optional[dict] = None, body=None) -> requests.Response:
|
111
111
|
"""Send HTTP request over TCP transport.
|
112
112
|
|
113
113
|
Args:
|
@@ -134,12 +134,12 @@ class AppMeshClientTCP(AppMeshClient):
|
|
134
134
|
appmesh_request.http_method = method.value
|
135
135
|
appmesh_request.request_uri = path
|
136
136
|
appmesh_request.client_addr = socket.gethostname()
|
137
|
-
appmesh_request.headers[self.
|
137
|
+
appmesh_request.headers[self._HTTP_HEADER_KEY_USER_AGENT] = self._HTTP_USER_AGENT_TCP
|
138
138
|
|
139
139
|
# Add authentication token
|
140
140
|
token = self._get_access_token()
|
141
141
|
if token:
|
142
|
-
appmesh_request.headers[self.
|
142
|
+
appmesh_request.headers[self._HTTP_HEADER_KEY_AUTH] = token
|
143
143
|
|
144
144
|
# Add custom headers
|
145
145
|
if header:
|
@@ -171,17 +171,17 @@ class AppMeshClientTCP(AppMeshClient):
|
|
171
171
|
response.headers = appmesh_resp.headers
|
172
172
|
|
173
173
|
# Set response content
|
174
|
-
# response.encoding = self.
|
174
|
+
# response.encoding = self._ENCODING_UTF8 # only need when charset not in appmesh_resp.body_msg_type
|
175
175
|
if isinstance(appmesh_resp.body, bytes):
|
176
176
|
response._content = appmesh_resp.body
|
177
177
|
else:
|
178
|
-
response._content = str(appmesh_resp.body).encode(self.
|
178
|
+
response._content = str(appmesh_resp.body).encode(self._ENCODING_UTF8)
|
179
179
|
|
180
180
|
# Set content type
|
181
181
|
if appmesh_resp.body_msg_type:
|
182
182
|
response.headers["Content-Type"] = appmesh_resp.body_msg_type
|
183
183
|
|
184
|
-
return AppMeshClient.
|
184
|
+
return AppMeshClient._EncodingResponse(response)
|
185
185
|
|
186
186
|
def _apply_file_attributes(self, local_file: str, headers: dict) -> None:
|
187
187
|
"""Apply file attributes from headers to local file."""
|
@@ -232,15 +232,15 @@ class AppMeshClientTCP(AppMeshClient):
|
|
232
232
|
preserve_permissions: Apply remote file permissions/ownership locally.
|
233
233
|
"""
|
234
234
|
header = {
|
235
|
-
AppMeshClient.
|
236
|
-
self.
|
235
|
+
AppMeshClient._HTTP_HEADER_KEY_X_FILE_PATH: remote_file,
|
236
|
+
self._HTTP_HEADER_KEY_X_RECV_FILE_SOCKET: "true",
|
237
237
|
}
|
238
238
|
|
239
|
-
resp = self._request_http(AppMeshClient.
|
239
|
+
resp = self._request_http(AppMeshClient._Method.GET, path="/appmesh/file/download", header=header)
|
240
240
|
resp.raise_for_status()
|
241
241
|
|
242
|
-
if self.
|
243
|
-
raise ValueError(f"Server did not respond with socket transfer option: " f"{self.
|
242
|
+
if self._HTTP_HEADER_KEY_X_RECV_FILE_SOCKET not in resp.headers:
|
243
|
+
raise ValueError(f"Server did not respond with socket transfer option: " f"{self._HTTP_HEADER_KEY_X_RECV_FILE_SOCKET}")
|
244
244
|
|
245
245
|
# Download file chunks
|
246
246
|
with open(local_file, "wb") as fp:
|
@@ -267,9 +267,9 @@ class AppMeshClientTCP(AppMeshClient):
|
|
267
267
|
|
268
268
|
# Prepare headers
|
269
269
|
header = {
|
270
|
-
AppMeshClient.
|
270
|
+
AppMeshClient._HTTP_HEADER_KEY_X_FILE_PATH: remote_file,
|
271
271
|
"Content-Type": "text/plain",
|
272
|
-
self.
|
272
|
+
self._HTTP_HEADER_KEY_X_SEND_FILE_SOCKET: "true",
|
273
273
|
}
|
274
274
|
|
275
275
|
# Add file attributes if requested
|
@@ -277,16 +277,16 @@ class AppMeshClientTCP(AppMeshClient):
|
|
277
277
|
header.update(self._get_file_attributes(local_file))
|
278
278
|
|
279
279
|
# Initiate upload
|
280
|
-
resp = self._request_http(AppMeshClient.
|
280
|
+
resp = self._request_http(AppMeshClient._Method.POST, path="/appmesh/file/upload", header=header)
|
281
281
|
resp.raise_for_status()
|
282
282
|
|
283
|
-
if self.
|
284
|
-
raise ValueError(f"Server did not respond with socket transfer option: " f"{self.
|
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}")
|
285
285
|
|
286
286
|
# Upload file chunks
|
287
287
|
with open(local_file, "rb") as fp:
|
288
288
|
while True:
|
289
|
-
chunk_data = fp.read(self.
|
289
|
+
chunk_data = fp.read(self._TCP_BLOCK_SIZE)
|
290
290
|
if not chunk_data:
|
291
291
|
self.tcp_transport.send_message([]) # EOF signal
|
292
292
|
break
|
@@ -40,7 +40,7 @@ class AppMeshServer(metaclass=abc.ABCMeta):
|
|
40
40
|
def __init__(
|
41
41
|
self,
|
42
42
|
rest_url: str = "https://127.0.0.1:6060",
|
43
|
-
rest_ssl_verify: Union[bool, str] = AppMeshClient.
|
43
|
+
rest_ssl_verify: Union[bool, str] = AppMeshClient._DEFAULT_SSL_CA_CERT_PATH,
|
44
44
|
rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = None,
|
45
45
|
rest_timeout: Tuple[float, float] = (60, 300),
|
46
46
|
*,
|
@@ -81,7 +81,7 @@ class AppMeshServer(metaclass=abc.ABCMeta):
|
|
81
81
|
|
82
82
|
while True:
|
83
83
|
resp = self._client._request_http(
|
84
|
-
AppMeshClient.
|
84
|
+
AppMeshClient._Method.GET,
|
85
85
|
path=path,
|
86
86
|
query=query_params,
|
87
87
|
)
|
@@ -106,7 +106,7 @@ class AppMeshServer(metaclass=abc.ABCMeta):
|
|
106
106
|
query_params = {"process_key": pkey}
|
107
107
|
|
108
108
|
resp = self._client._request_http(
|
109
|
-
AppMeshClient.
|
109
|
+
AppMeshClient._Method.PUT,
|
110
110
|
path=path,
|
111
111
|
query=query_params,
|
112
112
|
body=result,
|
@@ -18,7 +18,7 @@ class AppMeshServerTCP(AppMeshServer):
|
|
18
18
|
|
19
19
|
def __init__(
|
20
20
|
self,
|
21
|
-
rest_ssl_verify: Union[bool, str] = AppMeshClient.
|
21
|
+
rest_ssl_verify: Union[bool, str] = AppMeshClient._DEFAULT_SSL_CA_CERT_PATH,
|
22
22
|
rest_ssl_client_cert: Optional[Union[str, Tuple[str, str]]] = None,
|
23
23
|
tcp_address: Tuple[str, int] = ("127.0.0.1", 6059),
|
24
24
|
*,
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import io
|
2
2
|
import os
|
3
|
-
import sys
|
4
3
|
import setuptools
|
5
4
|
|
6
5
|
readme_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../../README.md")
|
@@ -10,7 +9,7 @@ with io.open(os.path.abspath(readme_path), mode="r", encoding="utf-8") as fh:
|
|
10
9
|
|
11
10
|
def get_version():
|
12
11
|
"""PyPI package version"""
|
13
|
-
return "1.6.
|
12
|
+
return "1.6.16"
|
14
13
|
|
15
14
|
|
16
15
|
# Dependencies
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|