appmesh 1.3.5__py3-none-any.whl → 1.3.7__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/__init__.py CHANGED
@@ -1 +1,18 @@
1
- import sys
1
+ # __init__.py
2
+ """
3
+ App Mesh SDK package initializer.
4
+
5
+ This module exports the main client classes used to interact with the App Mesh API.
6
+
7
+ Example:
8
+ from appmesh import AppMeshClient, AppMeshClientTCP
9
+
10
+ client = AppMeshClient()
11
+ client_tcp = AppMeshClientTCP()
12
+ """
13
+
14
+ from .app import App
15
+ from .appmesh_client import AppMeshClient
16
+ from .appmesh_client_tcp import AppMeshClientTCP
17
+
18
+ __all__ = ["App", "AppMeshClient", "AppMeshClientTCP"]
appmesh/app.py ADDED
@@ -0,0 +1,226 @@
1
+ """Application definition"""
2
+
3
+ import json
4
+ import copy
5
+
6
+ from datetime import datetime
7
+ from typing import Optional
8
+ from enum import Enum, unique
9
+
10
+ # pylint: disable=line-too-long
11
+
12
+
13
+ class App(object):
14
+ """
15
+ Represents an application in App Mesh, including configuration, resource limitations, behaviors, and permissions.
16
+ """
17
+
18
+ @staticmethod
19
+ def _get_str_item(data: dict, key: str) -> Optional[str]:
20
+ """Retrieve a string value from a dictionary by key, if it exists and is a valid string."""
21
+ return data[key] if (data and key in data and data[key] and isinstance(data[key], str)) else None
22
+
23
+ @staticmethod
24
+ def _get_int_item(data: dict, key: str) -> Optional[int]:
25
+ """Retrieve an integer value from a dictionary by key, if it exists and is a valid integer."""
26
+ return int(data[key]) if (data and key in data and data[key] and isinstance(data[key], int)) else None
27
+
28
+ @staticmethod
29
+ def _get_bool_item(data: dict, key: str) -> Optional[bool]:
30
+ """Retrieve a boolean value from a dictionary by key, if it exists and is boolean-like."""
31
+ return bool(data[key]) if (data and key in data and data[key]) else None
32
+
33
+ @staticmethod
34
+ def _get_native_item(data: dict, key: str) -> Optional[object]:
35
+ """Retrieve a deep copy of a value from a dictionary by key, if it exists."""
36
+ return copy.deepcopy(data[key]) if (data and key in data and data[key]) else None
37
+
38
+ @unique
39
+ class Permission(Enum):
40
+ """Defines application permission levels."""
41
+
42
+ DENY = "1"
43
+ READ = "2"
44
+ WRITE = "3"
45
+
46
+ class Behavior(object):
47
+ """
48
+ Manages application error handling behavior, including exit and control behaviors.
49
+ """
50
+
51
+ @unique
52
+ class Action(Enum):
53
+ """Defines actions for application exit behaviors."""
54
+
55
+ RESTART = "restart"
56
+ STANDBY = "standby"
57
+ KEEPALIVE = "keepalive"
58
+ REMOVE = "remove"
59
+
60
+ def __init__(self, data=None) -> None:
61
+ if isinstance(data, (str, bytes, bytearray)):
62
+ data = json.loads(data)
63
+
64
+ self.exit = App._get_str_item(data, "exit")
65
+ """Default exit behavior, options: 'restart', 'standby', 'keepalive', 'remove'."""
66
+
67
+ self.control = App._get_native_item(data, "control") or {}
68
+ """Exit code specific behavior (e.g, --control 0:restart --control 1:standby), higher priority than default exit behavior"""
69
+
70
+ def set_exit_behavior(self, action: Action) -> None:
71
+ """Set default behavior for application exit."""
72
+ self.exit = action.value
73
+
74
+ def set_control_behavior(self, control_code: int, action: Action) -> None:
75
+ """Define behavior for specific exit codes."""
76
+ self.control[str(control_code)] = action.value
77
+
78
+ class DailyLimitation(object):
79
+ """
80
+ Defines application availability within a daily time range.
81
+ """
82
+
83
+ def __init__(self, data=None) -> None:
84
+ if isinstance(data, (str, bytes, bytearray)):
85
+ data = json.loads(data)
86
+
87
+ self.daily_start = App._get_int_item(data, "daily_start")
88
+ """Start time for application availability (e.g., 09:00:00+08)."""
89
+
90
+ self.daily_end = App._get_int_item(data, "daily_end")
91
+ """End time for application availability (e.g., 20:00:00+08)."""
92
+
93
+ def set_daily_range(self, start: datetime, end: datetime) -> None:
94
+ """Set the valid daily start and end times."""
95
+ self.daily_start = int(start.timestamp())
96
+ self.daily_end = int(end.timestamp())
97
+
98
+ class ResourceLimitation(object):
99
+ """
100
+ Defines application resource limits, such as CPU and memory usage.
101
+ """
102
+
103
+ def __init__(self, data=None) -> None:
104
+ if isinstance(data, (str, bytes, bytearray)):
105
+ data = json.loads(data)
106
+
107
+ self.cpu_shares = App._get_int_item(data, "cpu_shares")
108
+ """CPU shares, relative weight of CPU usage."""
109
+
110
+ self.memory_mb = App._get_int_item(data, "memory_mb")
111
+ """Physical memory limit in MB."""
112
+
113
+ self.memory_virt_mb = App._get_int_item(data, "memory_virt_mb")
114
+ """Virtual memory limit in MB."""
115
+
116
+ def __init__(self, data=None) -> None:
117
+ """Initialize an App instance with optional configuration data."""
118
+ if isinstance(data, (str, bytes, bytearray)):
119
+ data = json.loads(data)
120
+
121
+ self.name = App._get_str_item(data, "name")
122
+ """application name (unique)"""
123
+ self.command = App._get_str_item(data, "command")
124
+ """full command line with arguments"""
125
+ self.shell = App._get_bool_item(data, "shell")
126
+ """use shell mode, cmd can be more shell commands with string format"""
127
+ self.session_login = App._get_bool_item(data, "session_login")
128
+ """app run in session login mode"""
129
+ self.description = App._get_str_item(data, "description")
130
+ """application description string"""
131
+ self.metadata = App._get_native_item(data, "metadata")
132
+ """metadata string/JSON (input for application, pass to process stdin)"""
133
+ self.working_dir = App._get_str_item(data, "working_dir")
134
+ """working directory"""
135
+ self.status = App._get_int_item(data, "status")
136
+ """initial application status (true is enable, false is disabled)"""
137
+ self.docker_image = App._get_str_item(data, "docker_image")
138
+ """docker image which used to run command line (for docker container application)"""
139
+ self.stdout_cache_num = App._get_int_item(data, "stdout_cache_num")
140
+ """stdout file cache number"""
141
+ self.start_time = App._get_int_item(data, "start_time")
142
+ """start date time for app (ISO8601 time format, e.g., '2020-10-11T09:22:05')"""
143
+ self.end_time = App._get_int_item(data, "end_time")
144
+ """end date time for app (ISO8601 time format, e.g., '2020-10-11T10:22:05')"""
145
+ self.interval = App._get_int_item(data, "interval")
146
+ """start interval seconds for short running app, support ISO 8601 durations and cron expression (e.g., 'P1Y2M3DT4H5M6S' 'P5W' '* */5 * * * *')"""
147
+ self.cron = App._get_bool_item(data, "cron")
148
+ """indicate interval parameter use cron expression or not"""
149
+ self.daily_limitation = App.DailyLimitation(App._get_native_item(data, "daily_limitation"))
150
+ self.retention = App._get_str_item(data, "retention")
151
+ """extra timeout seconds for stopping current process, support ISO 8601 durations (e.g., 'P1Y2M3DT4H5M6S' 'P5W')."""
152
+ self.health_check_cmd = App._get_str_item(data, "health_check_cmd")
153
+ """health check script command (e.g., sh -x 'curl host:port/health', return 0 is health)"""
154
+ self.permission = App._get_int_item(data, "permission")
155
+ """application user permission, value is 2 bit integer: [group & other], each bit can be deny:1, read:2, write: 3."""
156
+ self.behavior = App.Behavior(App._get_native_item(data, "behavior"))
157
+
158
+ self.env = data.get("env", {}) if data else {}
159
+ """environment variables (e.g., -e env1=value1 -e env2=value2, APP_DOCKER_OPTS is used to input docker run parameters)"""
160
+ self.sec_env = data.get("sec_env", {}) if data else {}
161
+ """security environment variables, encrypt in server side with application owner's cipher"""
162
+ self.pid = App._get_int_item(data, "pid")
163
+ """process id used to attach to the running process"""
164
+ self.resource_limit = App.ResourceLimitation(App._get_native_item(data, "resource_limit"))
165
+
166
+ # Read-only attributes
167
+ self.owner = App._get_str_item(data, "owner")
168
+ """owner name"""
169
+ self.pstree = App._get_str_item(data, "pstree")
170
+ """process tree"""
171
+ self.container_id = App._get_str_item(data, "container_id")
172
+ """container id"""
173
+ self.memory = App._get_int_item(data, "memory")
174
+ """memory usage"""
175
+ self.cpu = App._get_int_item(data, "cpu")
176
+ """cpu usage"""
177
+ self.fd = App._get_int_item(data, "fd")
178
+ """file descriptor usage"""
179
+ self.last_start_time = App._get_int_item(data, "last_start_time")
180
+ """last start time"""
181
+ self.last_exit_time = App._get_int_item(data, "last_exit_time")
182
+ """last exit time"""
183
+ self.health = App._get_int_item(data, "health")
184
+ """health status"""
185
+ self.version = App._get_int_item(data, "version")
186
+ """version number"""
187
+ self.return_code = App._get_int_item(data, "return_code")
188
+ """last exit code"""
189
+
190
+ def set_valid_time(self, start: datetime, end: datetime) -> None:
191
+ """Define the valid time window for the application."""
192
+ self.start_time = int(start.timestamp()) if start else None
193
+ self.end_time = int(end.timestamp()) if end else None
194
+
195
+ def set_env(self, key: str, value: str, secure: bool = False) -> None:
196
+ """Set an environment variable, marking it secure if specified."""
197
+ (self.sec_env if secure else self.env)[key] = value
198
+
199
+ def set_permission(self, group_user: Permission, others_user: Permission) -> None:
200
+ """Define application permissions based on user roles."""
201
+ self.permission = int(group_user.value + others_user.value)
202
+
203
+ def __str__(self) -> str:
204
+ """Return a JSON string representation of the application."""
205
+ return json.dumps(self.json())
206
+
207
+ def json(self) -> dict:
208
+ """Convert the application data into a JSON-compatible dictionary, removing empty items."""
209
+ output = copy.deepcopy(self.__dict__)
210
+ output["behavior"] = self.behavior.__dict__
211
+ output["daily_limitation"] = self.daily_limitation.__dict__
212
+ output["resource_limit"] = self.resource_limit.__dict__
213
+
214
+ def clean_empty(data: dict) -> None:
215
+ keys_to_delete = []
216
+ for key, value in data.items():
217
+ if isinstance(value, dict) and key != "metadata":
218
+ clean_empty(value) # Recursive call (without check user metadata)
219
+ if data[key] in [None, "", {}]:
220
+ keys_to_delete.append(key) # Mark keys for deletion
221
+
222
+ for key in keys_to_delete: # Delete keys after the loop to avoid modifying dict during iteration
223
+ del data[key]
224
+
225
+ clean_empty(output)
226
+ return output
appmesh/app_output.py ADDED
@@ -0,0 +1,26 @@
1
+ """Application output information"""
2
+
3
+ from http import HTTPStatus
4
+ from typing import Optional
5
+
6
+ # pylint: disable=line-too-long
7
+
8
+
9
+ class AppOutput(object):
10
+ """
11
+ Represents the output information returned by the `app_output()` API, including the application's
12
+ stdout content, current read position, status code, and exit code.
13
+ """
14
+
15
+ def __init__(self, status_code: HTTPStatus, output: str, out_position: Optional[int], exit_code: Optional[int]) -> None:
16
+ self.status_code = status_code
17
+ """HTTP status code from the `app_output()` API request, indicating the result status."""
18
+
19
+ self.output = output
20
+ """Captured stdout content of the application as returned by the `app_output()` API."""
21
+
22
+ self.out_position = out_position
23
+ """Current read position in the application's stdout stream, or `None` if not applicable."""
24
+
25
+ self.exit_code = exit_code
26
+ """Exit code of the application, or `None` if the process is still running or hasn't exited."""
appmesh/app_run.py ADDED
@@ -0,0 +1,48 @@
1
+ """Application run object"""
2
+
3
+ from contextlib import contextmanager
4
+
5
+ # pylint: disable=line-too-long
6
+
7
+
8
+ class AppRun(object):
9
+ """
10
+ Represents an application run object initiated by `run_async()` for monitoring and retrieving
11
+ the result of a remote application run.
12
+ """
13
+
14
+ def __init__(self, client, app_name: str, process_id: str):
15
+ self.app_name = app_name
16
+ """Name of the application associated with this run."""
17
+
18
+ self.proc_uid = process_id
19
+ """Unique process ID from `run_async()`."""
20
+
21
+ self._client = client
22
+ """Instance of `AppMeshClient` used to manage this application run."""
23
+
24
+ self._forwarding_host = client.forwarding_host
25
+ """Target server for the application run, used for forwarding."""
26
+
27
+ @contextmanager
28
+ def forwarding_host(self):
29
+ """Context manager to override the `forwarding_host` for the duration of the run."""
30
+ original_value = self._client.forwarding_host
31
+ self._client.forwarding_host = self._forwarding_host
32
+ try:
33
+ yield
34
+ finally:
35
+ self._client.forwarding_host = original_value
36
+
37
+ def wait(self, stdout_print: bool = True, timeout: int = 0) -> int:
38
+ """Wait for the asynchronous run to complete.
39
+
40
+ Args:
41
+ stdout_print (bool, optional): If `True`, prints remote stdout to local. Defaults to `True`.
42
+ timeout (int, optional): Maximum time to wait in seconds. If `0`, waits until completion. Defaults to `0`.
43
+
44
+ Returns:
45
+ int: Exit code if the process finishes successfully. Returns `None` on timeout or exception.
46
+ """
47
+ with self.forwarding_host():
48
+ return self._client.run_async_wait(self, stdout_print, timeout)