appmesh 1.6.12__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.
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: appmesh
3
+ Version: 1.6.12
4
+ Summary: Client SDK for App Mesh
5
+ Home-page: https://github.com/laoshanxi/app-mesh
6
+ Author: laoshanxi
7
+ Author-email: 178029200@qq.com
8
+ License: MIT
9
+ Keywords: appmesh AppMesh app-mesh
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.6
12
+ Classifier: Operating System :: OS Independent
13
+ Requires-Python: >=3
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: requests
16
+ Requires-Dist: msgpack
17
+ Requires-Dist: requests_toolbelt
18
+ Requires-Dist: aniso8601
19
+ Requires-Dist: PyJWT
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: keywords
27
+ Dynamic: license
28
+ Dynamic: requires-dist
29
+ Dynamic: requires-python
30
+ Dynamic: summary
31
+
32
+ īģŋ[![language.badge]][language.url] [![standard.badge]][standard.url] [![unittest.badge]][unittest.url] [![docker.badge]][docker.url] [![cockpit.badge]][cockpit.url]
33
+ [![Documentation Status](https://readthedocs.org/projects/app-mesh/badge/?version=latest)](https://app-mesh.readthedocs.io/en/latest/?badge=latest) [![Join the chat at https://gitter.im/app-mesh/community](https://badges.gitter.im/app-mesh/community.svg)](https://gitter.im/app-mesh/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
34
+ <a href="https://scan.coverity.com/projects/laoshanxi-app-mesh">
35
+ <img alt="Coverity Scan Build Status"
36
+ src="https://img.shields.io/coverity/scan/21528.svg"/>
37
+ </a>
38
+ [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/laoshanxi/app-mesh/badge)](https://api.securityscorecards.dev/projects/github.com/laoshanxi/app-mesh)
39
+ [![release.badge]][release.url] [![pypi.badge]][pypi.url] [![npm.badge]][npm.url]
40
+
41
+ # App Mesh: Advanced Application Management Platform
42
+
43
+ <div align=center><img src="https://github.com/laoshanxi/picture/raw/master/appmesh/whatis.gif" align=center /></div>
44
+ App Mesh is an open-source, multi-tenant application management platform designed for cloud-native environments. It efficiently manages, schedules, and monitors both microservices and traditional applications, offering a lightweight alternative to Kubernetes. App Mesh bridges the gap between simple process managers and complex container orchestration systems, making it ideal for organizations seeking to modernize their infrastructure without adopting full container-native complexity. Supporting both containerized and native applications, it provides a versatile solution for diverse enterprise needs.
45
+
46
+ <div align=center><img src="https://github.com/laoshanxi/picture/raw/master/appmesh/diagram.png" align=center /></div>
47
+
48
+ ## Features
49
+
50
+ Feature | Description
51
+ ---|---
52
+ Application management | 🧩 <b>Application Management (CURD) with Full Remote Control</b> – including cgroup, OS user, environment variables, Docker, stdin, and stdout – along with comprehensive monitoring (start counts, exit codes, error messages, health checks). <br> 🧩 <b>Fine-Grained Application Behavior Control & Scheduling</b> – supports long- and short-running tasks, periodic jobs, cron schedules, custom timings, and robust error handling. <br> 🧩 <b>Multi-Tenancy</b> – built-in user ownership model and access controls. <br> 🧩 <b>Unified Access Interface</b> – interact via [CLI](https://app-mesh.readthedocs.io/en/latest/CLI.html), [REST](https://app-mesh.readthedocs.io/en/latest/Development.html#rest-apis), [SDK](https://github.com/laoshanxi/app-mesh/tree/main/src/sdk) or [WebGUI](https://github.com/laoshanxi/app-mesh-ui).<br>
53
+ Security | 🔐 Authentication: [LDAP](https://app-mesh.readthedocs.io/en/latest/LDAP.html), [OAuth](src/sdk/python/test/test_oauth2.py), [2FA](https://app-mesh.readthedocs.io/en/latest/MFA.html), YAML-based storage (local file or Consul for clustering) <br> 🔐 Authorization: [JWT](https://app-mesh.readthedocs.io/en/latest/JWT.html), [RBAC](https://app-mesh.readthedocs.io/en/latest/USER_ROLE.html), multi-tenant isolation <br> 🔐 Protection: SSL/TLS for TCP/HTTP, CSRF tokens, HMAC with PSK for non-token verification
54
+ Cloud native | Schedule cloud-level applications to run on multiple hosts with resource size requests. <br> đŸŒŠī¸ [Prometheus Exporter (build-in)](https://app-mesh.readthedocs.io/en/latest/PROMETHEUS.html) <br> đŸŒŠī¸ [Grafana SimpleJson datasource](https://app-mesh.readthedocs.io/en/latest/GrafanaDataSource.html) <br> đŸŒŠī¸ [Grafana Loki](https://app-mesh.readthedocs.io/en/latest/Loki.html) <br>đŸŒŠī¸ [Dockerfile](https://github.com/laoshanxi/app-mesh/blob/main/Dockerfile)
55
+ Micro service application | 🧱 [Consul micro-service cluster management](https://app-mesh.readthedocs.io/en/latest/CONSUL.html)
56
+ Extra Features | Collect host/app resource usage <br> Remote shell command execution <br> File upload/download interface <br> Hot-update support `systemctl reload appmesh` <br> Bash completion <br> Reverse proxy <br> 🌐[Web GUI](https://github.com/laoshanxi/app-mesh-ui)
57
+ Platform support | X86_64 <br> ARM32 <br> ARM64
58
+ SDK | [Python](https://app-mesh.readthedocs.io/en/latest/api/appmesh.html#module-appmesh.client_http) <br> [Golang](https://github.com/laoshanxi/app-mesh/blob/main/src/sdk/go/client_http.go) <br> [JavaScript](https://www.npmjs.com/package/appmesh) <br> [Java](https://github.com/laoshanxi/app-mesh/packages/2227502) <br> [Swagger OpenAPI Specification](https://petstore.swagger.io/?url=https://raw.githubusercontent.com/laoshanxi/app-mesh/main/src/daemon/rest/openapi.yaml)
59
+
60
+ ## Getting started
61
+
62
+ Refer to the [Installation doc](https://app-mesh.readthedocs.io/en/latest/Install.html) to learn how to install App Mesh via Docker Compose or natively and set up an App Mesh cluster.
63
+
64
+ ## Documentation
65
+
66
+ - [Read the Docs](https://app-mesh.readthedocs.io/)
67
+ - [REST API](https://app-mesh.readthedocs.io/en/latest/Development.html#rest-apis)
68
+ - [Command lines](https://app-mesh.readthedocs.io/en/latest/CLI.html)
69
+ - [Security](https://app-mesh.readthedocs.io/en/latest/Security.html)
70
+
71
+ ## Comparison
72
+
73
+ ### Standalone mode
74
+
75
+ | Feature | App Mesh | [Supervisor](http://supervisord.org/) | [crontab](https://crontab.guru/) |
76
+ | ------------------------ | -------- | ------------------------------------- | -------------------------------- |
77
+ | Accuracy | Seconds | Seconds | Minutes |
78
+ | Language | C++11 | Python | C |
79
+ | Web GUI | √ | √ |
80
+ | Command lines | √ | √ | √ |
81
+ | SDK | √ | |
82
+ | Cron schedule expression | √ | | √ |
83
+ | Manage daemon process | | | √ |
84
+ | Manage docker app | √ | |
85
+ | Start check (avoid leak) | √ | √ |
86
+ | Session login | | |
87
+ | Manage stdout/stderr | √ | √ |
88
+ | Health check | √ | |
89
+ | Rich control options | √ | |
90
+ | Authentication | √ | √ |
91
+ | Multi-tenant | √ | | √ |
92
+
93
+ ### Cluster mode
94
+
95
+ | Feature | App Mesh | Kubernetes |
96
+ | ----------------- | -------- | ---------- |
97
+ | Easy deploy | √ |
98
+ | More features | | √ |
99
+ | Non-container app | √ |
100
+ | Service expose | √ | √ |
101
+ | Scheduler | √ | √ |
102
+ | Definition file | YAML | YAML |
103
+ | GUI | √ | √ |
104
+ | Virtual Network | | √ |
105
+ | Monitor tools | √ | √ |
106
+ | [Remote task](https://app-mesh.readthedocs.io/en/latest/RemoteTask.html) | √ | |
107
+
108
+ ---
109
+
110
+ ### Mind diagram
111
+
112
+ ![mind-diagram](https://github.com/laoshanxi/picture/raw/master/appmesh/mind.png)
113
+
114
+ ---
115
+
116
+ ## Success
117
+
118
+ - [Build a powerful monitor system with Grafana/Prometheus/Loki](https://app-mesh.readthedocs.io/en/latest/success/build_powerful_monitor_system_with_Grafana_Prometheus_Loki.html)
119
+ - [Customize application start behavior](https://app-mesh.readthedocs.io/en/latest/success/customize_app_startup_behavior.html)
120
+ - [Open service broker support local PV for Kubernetes](https://app-mesh.readthedocs.io/en/latest/success/open_service_broker_support_local_pv_for_K8S.html)
121
+ - [Promote native application to microservice application](https://app-mesh.readthedocs.io/en/latest/success/promote_native_app_to_microservice_app.html)
122
+ - [Secure REST file server](https://app-mesh.readthedocs.io/en/latest/success/secure_REST_file_server.html)
123
+ - [Standalone JWT server](https://app-mesh.readthedocs.io/en/latest/success/standalone_JWT_server.html)
124
+ - [Kubernetes run none-container applications](https://app-mesh.readthedocs.io/en/latest/success/kubernetes_run_native_application.html)
125
+ - [Remote execute](https://app-mesh.readthedocs.io/en/latest/success/remote_run_cli_and_python.html)
126
+ - [Python parallel run](https://app-mesh.readthedocs.io/en/latest/success/python_parallel_run.html)
127
+ - [Secure consul cluster](https://app-mesh.readthedocs.io/en/latest/success/secure_consul_cluster.html)
128
+ - [JWT service with REST and UI](https://github.com/laoshanxi/app-mesh/blob/main/script/docker-compose-auth-service.yaml)
129
+ - [Remote task execute](https://app-mesh.readthedocs.io/en/latest/RemoteTask.html)
130
+
131
+ ---
132
+
133
+ ## Library dependency
134
+
135
+ - [MessagePack](https://msgpack.org/)
136
+ - [boostorg/boost](https://github.com/boostorg/boost)
137
+ - [ACE_TAO/ACE](https://github.com/DOCGroup/ACE_TAO)
138
+ - [Thalhammer/jwt-cpp](https://github.com/Thalhammer/jwt-cpp)
139
+ - [nlohmann/json](https://json.nlohmann.me)
140
+ - [yaml-cpp](https://github.com/jbeder/yaml-cpp)
141
+ - [nfpm](https://github.com/goreleaser/nfpm)
142
+ - [jupp0r/prometheus-cpp](https://github.com/jupp0r/prometheus-cpp)
143
+ - [zemasoft/wildcards](https://github.com/zemasoft/wildcards)
144
+ - [mariusbancila/croncpp](https://github.com/mariusbancila/croncpp)
145
+ - [log4cpp](http://log4cpp.sourceforge.net)
146
+ - [Crypto++](https://www.cryptopp.com)
147
+ - [ldap-cpp](https://github.com/AndreyBarmaley/ldap-cpp)
148
+
149
+ [language.url]: https://isocpp.org/
150
+ [language.badge]: https://img.shields.io/badge/language-C++-blue.svg
151
+ [standard.url]: https://en.wikipedia.org/wiki/C%2B%2B#Standardization
152
+ [standard.badge]: https://img.shields.io/badge/C%2B%2B-11%2F14%2F17-blue.svg
153
+ [release.url]: https://github.com/laoshanxi/app-mesh/releases
154
+ [release.badge]: https://img.shields.io/github/v/release/laoshanxi/app-mesh?label=Github%20package
155
+ [docker.url]: https://hub.docker.com/repository/docker/laoshanxi/appmesh
156
+ [docker.badge]: https://img.shields.io/docker/pulls/laoshanxi/appmesh.svg
157
+ [cockpit.url]: https://github.com/laoshanxi/app-mesh-ui
158
+ [cockpit.badge]: https://img.shields.io/badge/Cockpit-app--mesh--ui-blue?logo=appveyor
159
+ [unittest.url]: https://github.com/catchorg/Catch2
160
+ [unittest.badge]: https://img.shields.io/badge/UnitTest-Catch2-blue?logo=appveyor
161
+ [pypi.badge]: https://img.shields.io/pypi/v/appmesh?label=PyPI%3Aappmesh
162
+ [pypi.url]: https://pypi.org/project/appmesh/
163
+ [npm.badge]: https://img.shields.io/npm/v/appmesh?label=npm%3Aappmesh
164
+ [npm.url]: https://www.npmjs.com/package/appmesh
@@ -0,0 +1,74 @@
1
+ # __init__.py
2
+ """
3
+ App Mesh SDK package initializer with lazy loading support.
4
+
5
+ Example:
6
+ from appmesh import AppMeshClient
7
+ client = AppMeshClient()
8
+ """
9
+
10
+ import sys
11
+ from types import ModuleType
12
+ from typing import TYPE_CHECKING
13
+ from importlib import import_module
14
+
15
+ __all__ = [
16
+ "App",
17
+ "AppMeshClient",
18
+ "AppMeshClientTCP",
19
+ "AppMeshClientOAuth",
20
+ "AppMeshServer",
21
+ "AppMeshServerTCP",
22
+ ]
23
+
24
+ # Lazy import configuration
25
+ _LAZY_IMPORTS = {
26
+ "App": ("app", "App"), # from .app import App
27
+ "AppMeshClient": ("client_http", "AppMeshClient"), # from .client_http import AppMeshClient
28
+ "AppMeshClientTCP": ("client_tcp", "AppMeshClientTCP"), # from .client_tcp import AppMeshClientTCP
29
+ "AppMeshClientOAuth": ("client_http_oauth", "AppMeshClientOAuth"), # from .client_http_oauth import AppMeshClientOAuth
30
+ "AppMeshServer": ("server_http", "AppMeshServer"), # from .server_http import AppMeshServer
31
+ "AppMeshServerTCP": ("server_tcp", "AppMeshServerTCP"), # from .server_tcp import AppMeshServerTCP
32
+ }
33
+
34
+ if TYPE_CHECKING:
35
+ # Type checking imports (not executed at runtime)
36
+ from .app import App # noqa: F401
37
+ from .client_http import AppMeshClient # noqa: F401
38
+ from .client_tcp import AppMeshClientTCP # noqa: F401
39
+ from .client_http_oauth import AppMeshClientOAuth # noqa: F401
40
+ from .server_http import AppMeshServer # noqa: F401
41
+ from .server_tcp import AppMeshServerTCP # noqa: F401
42
+
43
+
44
+ def _lazy_import(name: str):
45
+ """
46
+ Internal helper for lazy import resolution using PEP 562.
47
+ Only imports modules when accessed, improving startup time.
48
+ """
49
+ if name in _LAZY_IMPORTS:
50
+ module_name, attr_name = _LAZY_IMPORTS[name]
51
+ module = import_module(f".{module_name}", __name__)
52
+ globals()[name] = getattr(module, attr_name)
53
+ return globals()[name]
54
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
55
+
56
+
57
+ def __dir__():
58
+ """Provide tab-completion support for lazy-loaded attributes."""
59
+ return sorted(__all__ + list(globals().keys()))
60
+
61
+
62
+ if sys.version_info >= (3, 7):
63
+ __getattr__ = _lazy_import
64
+ else:
65
+ # Python 3.6 compatibility via module replacement
66
+ class _LazyModule(ModuleType):
67
+ def __getattr__(self, name):
68
+ return _lazy_import(name)
69
+
70
+ def __dir__(self):
71
+ return sorted(__all__ + list(globals().keys()))
72
+
73
+ sys.modules[__name__] = _LazyModule(__name__)
74
+ sys.modules[__name__].__dict__.update(globals())
@@ -0,0 +1,252 @@
1
+ # app.py
2
+ """Application definition"""
3
+
4
+ import json
5
+ import copy
6
+ from datetime import datetime
7
+ from typing import Optional, Any, Dict
8
+ from enum import Enum, unique
9
+
10
+
11
+ def _get_str(data: Optional[dict], key: str) -> Optional[str]:
12
+ """Retrieve a string value from a dictionary by key, if it exists and is a valid string."""
13
+ if not data or key not in data:
14
+ return None
15
+ value = data[key]
16
+ return value if value and isinstance(value, str) else None
17
+
18
+
19
+ def _get_int(data: Optional[dict], key: str) -> Optional[int]:
20
+ """Retrieve an integer value from a dictionary by key, if it exists and is a valid integer."""
21
+ if not data or key not in data or data[key] is None:
22
+ return None
23
+
24
+ value = data[key]
25
+ if isinstance(value, int):
26
+ return value
27
+ if isinstance(value, str) and value.isdigit():
28
+ return int(value)
29
+ return None
30
+
31
+
32
+ def _get_bool(data: Optional[dict], key: str) -> Optional[bool]:
33
+ """Retrieve a boolean value from a dictionary by key, if it exists and is boolean-like."""
34
+ if not data or key not in data or data[key] is None:
35
+ return None
36
+ return bool(data[key])
37
+
38
+
39
+ def _get_item(data: Optional[dict], key: str) -> Optional[Any]:
40
+ """Retrieve a deep copy of a value from a dictionary by key, if it exists."""
41
+ if not data or key not in data or data[key] is None:
42
+ return None
43
+ return copy.deepcopy(data[key])
44
+
45
+
46
+ class App:
47
+ """
48
+ An application in App Mesh, include all the process attributes,
49
+ resource limitations, behaviors, and permissions.
50
+ """
51
+
52
+ @unique
53
+ class Permission(Enum):
54
+ """Application permission levels."""
55
+
56
+ DENY = "1"
57
+ READ = "2"
58
+ WRITE = "3"
59
+
60
+ class Behavior:
61
+ """
62
+ Application error handling behavior, including exit and control behaviors.
63
+ """
64
+
65
+ @unique
66
+ class Action(Enum):
67
+ """Actions for application exit behaviors."""
68
+
69
+ RESTART = "restart"
70
+ STANDBY = "standby"
71
+ KEEPALIVE = "keepalive"
72
+ REMOVE = "remove"
73
+
74
+ def __init__(self, data: Optional[dict] = None) -> None:
75
+ if isinstance(data, (str, bytes, bytearray)):
76
+ data = json.loads(data)
77
+
78
+ self.exit = _get_str(data, "exit")
79
+ """Default exit behavior, options: 'restart', 'standby', 'keepalive', 'remove'."""
80
+
81
+ self.control = _get_item(data, "control") or {}
82
+ """Exit code specific behavior (e.g, --control 0:restart --control 1:standby), higher priority than default exit behavior"""
83
+
84
+ def set_exit_behavior(self, action: "App.Behavior.Action") -> None:
85
+ """Set default behavior for application exit."""
86
+ self.exit = action.value
87
+
88
+ def set_control_behavior(self, control_code: int, action: "App.Behavior.Action") -> None:
89
+ """Define behavior for specific exit codes."""
90
+ self.control[str(control_code)] = action.value
91
+
92
+ class DailyLimitation:
93
+ """
94
+ Application availability within a daily time range.
95
+ """
96
+
97
+ def __init__(self, data: Optional[dict] = None) -> None:
98
+ if isinstance(data, (str, bytes, bytearray)):
99
+ data = json.loads(data)
100
+
101
+ self.daily_start = _get_int(data, "daily_start")
102
+ """Start time for application availability (e.g., 09:00:00+08)."""
103
+
104
+ self.daily_end = _get_int(data, "daily_end")
105
+ """End time for application availability (e.g., 09:00:00+08)."""
106
+
107
+ def set_daily_range(self, start: datetime, end: datetime) -> None:
108
+ """Set the valid daily start and end times."""
109
+ self.daily_start = int(start.timestamp())
110
+ self.daily_end = int(end.timestamp())
111
+
112
+ class ResourceLimitation:
113
+ """
114
+ Application resource limits, such as CPU and memory usage.
115
+ """
116
+
117
+ def __init__(self, data: Optional[dict] = None) -> None:
118
+ if isinstance(data, (str, bytes, bytearray)):
119
+ data = json.loads(data)
120
+
121
+ self.cpu_shares = _get_int(data, "cpu_shares")
122
+ """CPU shares, relative weight of CPU usage."""
123
+
124
+ self.memory_mb = _get_int(data, "memory_mb")
125
+ """Physical memory limit in MB."""
126
+
127
+ self.memory_virt_mb = _get_int(data, "memory_virt_mb")
128
+ """Virtual memory limit in MB."""
129
+
130
+ def __init__(self, data: Optional[dict] = None) -> None:
131
+ """Initialize an App instance with optional configuration data."""
132
+ if isinstance(data, (str, bytes, bytearray)):
133
+ data = json.loads(data)
134
+
135
+ # Application configuration
136
+ self.name = _get_str(data, "name")
137
+ """application name (unique)"""
138
+ self.command = _get_str(data, "command")
139
+ """full command line with arguments"""
140
+ self.shell = _get_bool(data, "shell")
141
+ """use shell mode, cmd can be more shell commands with string format"""
142
+ self.session_login = _get_bool(data, "session_login")
143
+ """app run in session login mode"""
144
+ self.description = _get_str(data, "description")
145
+ """application description string"""
146
+ self.metadata = _get_item(data, "metadata")
147
+ """metadata string/JSON (input for application, pass to process stdin)"""
148
+ self.working_dir = _get_str(data, "working_dir")
149
+ """working directory"""
150
+ self.status = _get_int(data, "status")
151
+ """initial application status (true is enable, false is disabled)"""
152
+ self.docker_image = _get_str(data, "docker_image")
153
+ """docker image which used to run command line (for docker container application)"""
154
+ self.stdout_cache_num = _get_int(data, "stdout_cache_num")
155
+ """stdout file cache number"""
156
+ self.start_time = _get_int(data, "start_time")
157
+ """start date time for app (ISO8601 time format, e.g., '2020-10-11T09:22:05')"""
158
+ self.end_time = _get_int(data, "end_time")
159
+ """end date time for app (ISO8601 time format, e.g., '2020-10-11T10:22:05')"""
160
+ self.interval = _get_int(data, "interval")
161
+ """start interval seconds for short running app, support ISO 8601 durations and cron expression (e.g., 'P1Y2M3DT4H5M6S' 'P5W' '* */5 * * * *')"""
162
+ self.cron = _get_bool(data, "cron")
163
+ """indicate interval parameter use cron expression or not"""
164
+ self.daily_limitation = App.DailyLimitation(_get_item(data, "daily_limitation"))
165
+ self.retention = _get_str(data, "retention")
166
+ """extra timeout seconds for stopping current process, support ISO 8601 durations (e.g., 'P1Y2M3DT4H5M6S' 'P5W')."""
167
+ self.health_check_cmd = _get_str(data, "health_check_cmd")
168
+ """health check script command (e.g., sh -x 'curl host:port/health', return 0 is health)"""
169
+ self.permission = _get_int(data, "permission")
170
+ """application user permission, value is 2 bit integer: [group & other], each bit can be deny:1, read:2, write: 3."""
171
+ self.behavior = App.Behavior(_get_item(data, "behavior"))
172
+
173
+ self.env = data.get("env", {}) if data else {}
174
+ """environment variables (e.g., -e env1=value1 -e env2=value2, APP_DOCKER_OPTS is used to input docker run parameters)"""
175
+ self.sec_env = data.get("sec_env", {}) if data else {}
176
+ """security environment variables, encrypt in server side with application owner's cipher"""
177
+ self.pid = _get_int(data, "pid")
178
+ """process id used to attach to the running process"""
179
+ self.resource_limit = App.ResourceLimitation(_get_item(data, "resource_limit"))
180
+
181
+ # Read-only attributes
182
+ self.owner = _get_str(data, "owner")
183
+ """owner name"""
184
+ self.user = _get_str(data, "pid_user")
185
+ """process user name"""
186
+ self.pstree = _get_str(data, "pstree")
187
+ """process tree"""
188
+ self.container_id = _get_str(data, "container_id")
189
+ """container id"""
190
+ self.memory = _get_int(data, "memory")
191
+ """memory usage"""
192
+ self.cpu = _get_int(data, "cpu")
193
+ """cpu usage"""
194
+ self.fd = _get_int(data, "fd")
195
+ """file descriptor usage"""
196
+ self.last_start_time = _get_int(data, "last_start_time")
197
+ """last start time"""
198
+ self.last_exit_time = _get_int(data, "last_exit_time")
199
+ """last exit time"""
200
+ self.health = _get_int(data, "health")
201
+ """health status"""
202
+ self.version = _get_int(data, "version")
203
+ """version number"""
204
+ self.return_code = _get_int(data, "return_code")
205
+ """last exit code"""
206
+ self.task_id = _get_int(data, "task_id")
207
+ """current task id"""
208
+ self.task_status = _get_str(data, "task_status")
209
+ """task status"""
210
+
211
+ def set_valid_time(self, start: Optional[datetime], end: Optional[datetime]) -> None:
212
+ """Define the valid time window for the application."""
213
+ self.start_time = int(start.timestamp()) if start else None
214
+ self.end_time = int(end.timestamp()) if end else None
215
+
216
+ def set_env(self, key: str, value: str, secure: bool = False) -> None:
217
+ """Set an environment variable, marking it secure if specified."""
218
+ target = self.sec_env if secure else self.env
219
+ target[key] = value
220
+
221
+ def set_permission(self, group_user: Permission, others_user: Permission) -> None:
222
+ """Define application permissions based on user roles."""
223
+ self.permission = int(group_user.value + others_user.value)
224
+
225
+ def __str__(self) -> str:
226
+ """Return a JSON string representation of the application."""
227
+ return json.dumps(self.json())
228
+
229
+ def json(self) -> Dict[str, Any]:
230
+ """Convert the application data into a JSON-compatible dictionary, removing empty items."""
231
+ output = copy.deepcopy(self.__dict__)
232
+ output["behavior"] = self.behavior.__dict__
233
+ output["daily_limitation"] = self.daily_limitation.__dict__
234
+ output["resource_limit"] = self.resource_limit.__dict__
235
+
236
+ self._clean_empty(output)
237
+ return output
238
+
239
+ @staticmethod
240
+ def _clean_empty(data: dict) -> None:
241
+ """Recursively remove None, empty string, and empty dict values from nested dictionaries (except 'metadata')."""
242
+ keys_to_delete = []
243
+ for key, value in data.items():
244
+ if isinstance(value, dict) and key != "metadata":
245
+ App._clean_empty(value)
246
+ if not value:
247
+ keys_to_delete.append(key)
248
+ elif value in (None, "", {}):
249
+ keys_to_delete.append(key)
250
+
251
+ for key in keys_to_delete:
252
+ del data[key]
@@ -0,0 +1,28 @@
1
+ # app_output.py
2
+ """Application output information."""
3
+
4
+ from dataclasses import dataclass
5
+ from http import HTTPStatus
6
+ from typing import Optional
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class AppOutput:
11
+ """
12
+ Output information returned by the `app_output()` API.
13
+
14
+ Includes the application's stdout, current read position,
15
+ HTTP status code, and process exit code.
16
+ """
17
+
18
+ status_code: HTTPStatus
19
+ """HTTP status code from the `app_output()` API request."""
20
+
21
+ output: str
22
+ """Captured stdout content of the application."""
23
+
24
+ out_position: Optional[int]
25
+ """Current read position in stdout stream, or None if not applicable."""
26
+
27
+ exit_code: Optional[int]
28
+ """Exit code of the application, or None if still running."""
@@ -0,0 +1,54 @@
1
+ # app_run.py
2
+ """Application run object for remote application execution."""
3
+
4
+ from contextlib import contextmanager
5
+ from typing import TYPE_CHECKING, Optional
6
+
7
+ if TYPE_CHECKING:
8
+ from .client_http import AppMeshClient
9
+
10
+
11
+ class AppRun:
12
+ """
13
+ Application run object for monitoring and retrieving results
14
+ of a remote application run initiated by `run_async()`.
15
+ """
16
+
17
+ def __init__(self, client: "AppMeshClient", app_name: str, process_id: str):
18
+ self.app_name = app_name
19
+ """Name of the application associated with this run."""
20
+
21
+ self.proc_uid = process_id
22
+ """Unique process ID from `run_async()`."""
23
+
24
+ self._client = client
25
+ self._forward_to = client.forward_to
26
+
27
+ @contextmanager
28
+ def forward_to(self):
29
+ """
30
+ Context manager to temporarily override the client's `forward_to` setting.
31
+
32
+ Ensures operations during this run use the correct target server,
33
+ then restores the original setting.
34
+ """
35
+ original_value = self._client.forward_to
36
+ self._client.forward_to = self._forward_to
37
+ try:
38
+ yield
39
+ finally:
40
+ self._client.forward_to = original_value
41
+
42
+ def wait(self, stdout_print: bool = True, timeout: int = 0) -> Optional[int]:
43
+ """
44
+ Wait for the asynchronous run to complete.
45
+
46
+ Args:
47
+ stdout_print: If True, prints remote stdout to local console.
48
+ timeout: Maximum time to wait in seconds. 0 means wait indefinitely.
49
+
50
+ Returns:
51
+ Exit code if the process finishes successfully, or None on timeout.
52
+ """
53
+ with self.forward_to():
54
+ return self._client.wait_for_async_run(self, stdout_print, timeout)
@@ -0,0 +1,10 @@
1
+ # appmesh_client.py
2
+
3
+ # Legacy Compatibility Layer
4
+ # These imports provide backward compatibility for older code that relies on
5
+ # AppMeshClient, App, and AppOutput classes. The updated implementation can be found
6
+ # in client_http.py, where these classes are now primarily maintained.
7
+
8
+ from .client_http import AppMeshClient
9
+ from .app import App
10
+ from .app_output import AppOutput