polaris-python 0.0.1.dev3__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.
- polaris/__init__.py +74 -0
- polaris/_version.py +16 -0
- polaris/api/__init__.py +55 -0
- polaris/api/_log_file.py +232 -0
- polaris/api/config.py +145 -0
- polaris/api/consumer.py +240 -0
- polaris/api/log.py +414 -0
- polaris/api/lossless.py +73 -0
- polaris/api/provider.py +152 -0
- polaris/api/skill.py +162 -0
- polaris/pkg/__init__.py +15 -0
- polaris/pkg/common/__init__.py +15 -0
- polaris/pkg/common/plugin.py +33 -0
- polaris/pkg/config/__init__.py +15 -0
- polaris/pkg/config/api.py +1205 -0
- polaris/pkg/config/default.py +92 -0
- polaris/pkg/config/event.py +76 -0
- polaris/pkg/config/loader.py +392 -0
- polaris/pkg/config/stat_reporter.py +100 -0
- polaris/pkg/flow/__init__.py +15 -0
- polaris/pkg/flow/admin_server.py +104 -0
- polaris/pkg/flow/async_flow.py +55 -0
- polaris/pkg/flow/base_flow.py +111 -0
- polaris/pkg/flow/impl.py +1277 -0
- polaris/pkg/flow/lossless.py +605 -0
- polaris/pkg/flow/notify.py +46 -0
- polaris/pkg/flow/register.py +210 -0
- polaris/pkg/flow/skill.py +110 -0
- polaris/pkg/flow/sync_flow.py +20 -0
- polaris/pkg/model/__init__.py +15 -0
- polaris/pkg/model/context.py +53 -0
- polaris/pkg/model/contract.py +89 -0
- polaris/pkg/model/core.py +51 -0
- polaris/pkg/model/engine.py +83 -0
- polaris/pkg/model/error.py +94 -0
- polaris/pkg/model/event.py +159 -0
- polaris/pkg/model/lossless.py +102 -0
- polaris/pkg/model/rule.py +152 -0
- polaris/pkg/model/server.py +288 -0
- polaris/pkg/model/service.py +820 -0
- polaris/pkg/model/skill.py +179 -0
- polaris/pkg/model/stat.py +168 -0
- polaris/pkg/model/util.py +58 -0
- polaris/pkg/version/__init__.py +18 -0
- polaris/plugin/__init__.py +32 -0
- polaris/plugin/circuit_breaker/__init__.py +19 -0
- polaris/plugin/circuit_breaker/base.py +54 -0
- polaris/plugin/circuit_breaker/composite.py +265 -0
- polaris/plugin/circuit_breaker/config.py +36 -0
- polaris/plugin/event/__init__.py +21 -0
- polaris/plugin/event/pushgateway.py +202 -0
- polaris/plugin/load_balancer/__init__.py +24 -0
- polaris/plugin/load_balancer/base.py +30 -0
- polaris/plugin/load_balancer/random.py +39 -0
- polaris/plugin/load_balancer/weighted_random.py +50 -0
- polaris/plugin/local_registry/__init__.py +18 -0
- polaris/plugin/local_registry/base.py +47 -0
- polaris/plugin/local_registry/file_cache.py +335 -0
- polaris/plugin/local_registry/local_cache.py +433 -0
- polaris/plugin/server_connector/__init__.py +20 -0
- polaris/plugin/server_connector/base.py +104 -0
- polaris/plugin/server_connector/convert.py +149 -0
- polaris/plugin/server_connector/grpc_connector/__init__.py +21 -0
- polaris/plugin/server_connector/grpc_connector/connection.py +401 -0
- polaris/plugin/server_connector/grpc_connector/grpc_client.py +384 -0
- polaris/plugin/server_connector/grpc_connector/tls.py +45 -0
- polaris/plugin/service_router/__init__.py +28 -0
- polaris/plugin/service_router/base.py +105 -0
- polaris/plugin/service_router/isolated.py +35 -0
- polaris/plugin/service_router/metadata.py +84 -0
- polaris/plugin/service_router/recover.py +62 -0
- polaris/plugin/service_router/util.py +54 -0
- polaris/plugin/skill/__init__.py +16 -0
- polaris/plugin/skill/connector.py +127 -0
- polaris/plugin/skill/persistence.py +94 -0
- polaris/plugin/stat_reporter/__init__.py +16 -0
- polaris/plugin/stat_reporter/base.py +34 -0
- polaris/plugin/stat_reporter/prometheus.py +375 -0
- polaris/plugin_manager.py +146 -0
- polaris_python-0.0.1.dev3.dist-info/METADATA +213 -0
- polaris_python-0.0.1.dev3.dist-info/RECORD +84 -0
- polaris_python-0.0.1.dev3.dist-info/WHEEL +5 -0
- polaris_python-0.0.1.dev3.dist-info/licenses/LICENSE +18 -0
- polaris_python-0.0.1.dev3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Tencent is pleased to support the open source community by making polaris-python available.
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2021 Tencent. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the BSD 3-Clause License (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# https://opensource.org/licenses/BSD-3-Clause
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software distributed
|
|
12
|
+
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
13
|
+
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
14
|
+
# specific language governing permissions and limitations under the License.
|
|
15
|
+
|
|
16
|
+
from typing import Any, Iterable
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ErrorCode:
|
|
20
|
+
# 正常
|
|
21
|
+
ErrCodeSuccess = 0
|
|
22
|
+
# 接口参数错误,API接口调用时,对接口参数进行校验失败后的错误码
|
|
23
|
+
ErrCodeAPIInvalidArgument = 1001
|
|
24
|
+
# 配置参数校验失败
|
|
25
|
+
ErrCodeAPIInvalidConfig = 1002
|
|
26
|
+
# 注册插件、初始化插件、获取插件、销毁插件时发生的错误
|
|
27
|
+
ErrCodePluginError = 1003
|
|
28
|
+
# API调用超时无正确返回
|
|
29
|
+
ErrCodeAPITimeoutError = 1004
|
|
30
|
+
# SDK状态错误,出现在SDK已经销毁还进行API调用
|
|
31
|
+
ErrCodeInvalidStateError = 1005
|
|
32
|
+
# 连接server成功,但是server返回了报错信息
|
|
33
|
+
ErrCodeServerError = 1006
|
|
34
|
+
# 连接服务端出现网络异常
|
|
35
|
+
ErrCodeNetworkError = 1007
|
|
36
|
+
# 服务熔断发生错误
|
|
37
|
+
ErrCodeCircuitBreakerError = 1008
|
|
38
|
+
# 一个实例中包含的信息不合法,如host信息无法正常解析
|
|
39
|
+
ErrCodeInstanceInfoError = 1009
|
|
40
|
+
# 当今行负载均衡、服务路由时,传入的服务没有实例信息
|
|
41
|
+
ErrCodeAPIInstanceNotFound = 1010
|
|
42
|
+
# 路由规则不合法
|
|
43
|
+
ErrCodeInvalidRouteRule = 1011
|
|
44
|
+
# 路由规则无法匹配
|
|
45
|
+
ErrCodeRouteRuleNotMatch = 1012
|
|
46
|
+
# server返回的信息不合法
|
|
47
|
+
ErrCodeInvalidResponse = 1013
|
|
48
|
+
# 获取MaxQueuingTime失败
|
|
49
|
+
ErrCodeMaxQueuingTimeInvalid = 1015
|
|
50
|
+
# server返回的信息不合法
|
|
51
|
+
ErrCodeDataNotChange = 1016
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ServerCode:
|
|
55
|
+
"""Polaris response codes used by SDK control flow."""
|
|
56
|
+
|
|
57
|
+
ExecuteSuccess = 200000
|
|
58
|
+
DataNotChange = 200001
|
|
59
|
+
NoNeedUpdate = 200002
|
|
60
|
+
ExistedResource = 400201
|
|
61
|
+
NotFoundResource = 400202
|
|
62
|
+
NotFoundService = 400301
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class SDKError(Exception):
|
|
66
|
+
"""SDK failure with a stable local code and optional server response code."""
|
|
67
|
+
|
|
68
|
+
def __init__(self, err_code: int, msg: str, server_code: int | None = None):
|
|
69
|
+
super().__init__(err_code, msg)
|
|
70
|
+
self.err_code = err_code
|
|
71
|
+
self.msg = msg
|
|
72
|
+
self.server_code = server_code
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def response_code(response: Any) -> int:
|
|
76
|
+
"""Return an integer code from a specification response."""
|
|
77
|
+
code = getattr(response, "code", None)
|
|
78
|
+
return int(getattr(code, "value", code or 0))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def response_info(response: Any) -> str:
|
|
82
|
+
"""Return response information from a protobuf wrapper."""
|
|
83
|
+
info = getattr(response, "info", None)
|
|
84
|
+
return str(getattr(info, "value", info or "Polaris server request failed"))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def ensure_response(response: Any, accepted: Iterable[int] = (ServerCode.ExecuteSuccess,)) -> Any:
|
|
88
|
+
"""Validate a Polaris response and preserve data-not-change semantics."""
|
|
89
|
+
code = response_code(response)
|
|
90
|
+
if code in accepted:
|
|
91
|
+
return response
|
|
92
|
+
if code in {ServerCode.DataNotChange, ServerCode.NoNeedUpdate}:
|
|
93
|
+
raise SDKError(ErrorCode.ErrCodeDataNotChange, response_info(response), server_code=code)
|
|
94
|
+
raise SDKError(ErrorCode.ErrCodeServerError, response_info(response), server_code=code)
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Tencent is pleased to support the open source community by making polaris-python available.
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2021 Tencent. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the BSD 3-Clause License (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# https://opensource.org/licenses/BSD-3-Clause
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
"""Context-owned event center for capabilities implemented by this SDK."""
|
|
18
|
+
|
|
19
|
+
from dataclasses import dataclass, field
|
|
20
|
+
from threading import RLock
|
|
21
|
+
import time
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SDKEventType:
|
|
25
|
+
"""Only event categories backed by implemented runtime behavior."""
|
|
26
|
+
|
|
27
|
+
INSTANCE = "INSTANCE"
|
|
28
|
+
CIRCUIT_BREAKING = "CIRCUIT_BREAKING"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SDKEventName:
|
|
32
|
+
INSTANCE_THREAD_END = "InstanceThreadEnd"
|
|
33
|
+
ON_SERVICE_ADDED = "OnServiceAdded"
|
|
34
|
+
ON_SERVICE_UPDATED = "OnServiceUpdated"
|
|
35
|
+
ON_SERVICE_DELETED = "OnServiceDeleted"
|
|
36
|
+
CIRCUIT_BREAKER_OPEN = "CircuitBreakerOpen"
|
|
37
|
+
CIRCUIT_BREAKER_HALF_OPEN = "CircuitBreakerHalfOpen"
|
|
38
|
+
CIRCUIT_BREAKER_CLOSE = "CircuitBreakerClose"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SDKEventStatus:
|
|
42
|
+
OPEN = "OPEN"
|
|
43
|
+
CLOSE = "CLOSE"
|
|
44
|
+
HALF_OPEN = "HALF_OPEN"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# Java EventConstants pairs this SDK can emit. Cache lifecycle names stay local.
|
|
48
|
+
_REMOTE_EVENT_KEYS = frozenset({
|
|
49
|
+
(SDKEventType.INSTANCE, SDKEventName.INSTANCE_THREAD_END),
|
|
50
|
+
(SDKEventType.CIRCUIT_BREAKING, SDKEventName.CIRCUIT_BREAKER_OPEN),
|
|
51
|
+
(SDKEventType.CIRCUIT_BREAKING, SDKEventName.CIRCUIT_BREAKER_HALF_OPEN),
|
|
52
|
+
(SDKEventType.CIRCUIT_BREAKING, SDKEventName.CIRCUIT_BREAKER_CLOSE),
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def is_remote_reportable(event) -> bool:
|
|
57
|
+
"""Return whether Java EventReporter would accept this event type and name."""
|
|
58
|
+
return (event.event_type, event.event_name) in _REMOTE_EVENT_KEYS
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True)
|
|
62
|
+
class SDKEvent:
|
|
63
|
+
"""One immutable event emitted by an implemented SDK capability."""
|
|
64
|
+
|
|
65
|
+
event_type: str
|
|
66
|
+
event_name: str
|
|
67
|
+
timestamp: int
|
|
68
|
+
client_id: str
|
|
69
|
+
namespace: str
|
|
70
|
+
service: str
|
|
71
|
+
client_ip: str = ""
|
|
72
|
+
old_value: object = None
|
|
73
|
+
new_value: object = None
|
|
74
|
+
instance_id: str = ""
|
|
75
|
+
host: str = ""
|
|
76
|
+
port: int = 0
|
|
77
|
+
previous_status: str = ""
|
|
78
|
+
current_status: str = ""
|
|
79
|
+
rule_name: str = ""
|
|
80
|
+
reason: str = ""
|
|
81
|
+
additional: dict = field(default_factory=dict)
|
|
82
|
+
|
|
83
|
+
def to_log_message(self):
|
|
84
|
+
"""Format an event using the Java and Go logger field order."""
|
|
85
|
+
identity = f"{self.instance_id}@{self.host}:{self.port}" if self.instance_id else ""
|
|
86
|
+
states = f" {self.previous_status}->{self.current_status}" if self.current_status else ""
|
|
87
|
+
return (f"{self.event_type}|{self.event_name}|{self.timestamp}|{self.client_id}|"
|
|
88
|
+
f"{self.client_ip}|{identity}{states}|{self.namespace}|{self.service}|{self.reason}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class EventCenter:
|
|
92
|
+
"""Dispatch context-local events without affecting SDK control flow."""
|
|
93
|
+
|
|
94
|
+
def __init__(self, value_context=None):
|
|
95
|
+
self.value_context = value_context
|
|
96
|
+
self.logger = value_context.logger.get_logger("event") if value_context.logger is not None else None
|
|
97
|
+
self._lock = RLock()
|
|
98
|
+
self._listeners = []
|
|
99
|
+
self.register_listener(self._log_event)
|
|
100
|
+
if self.logger is not None:
|
|
101
|
+
self.logger.info("event center initialized")
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def _listener_name(listener):
|
|
105
|
+
if hasattr(listener, "__name__"):
|
|
106
|
+
return listener.__name__
|
|
107
|
+
return type(listener).__name__
|
|
108
|
+
|
|
109
|
+
@staticmethod
|
|
110
|
+
def _invoke(listener, event):
|
|
111
|
+
method = getattr(listener, "on_sdk_event", None)
|
|
112
|
+
if method is not None:
|
|
113
|
+
return method(event)
|
|
114
|
+
return listener(event)
|
|
115
|
+
|
|
116
|
+
def register_listener(self, listener, event_type: str | None = None):
|
|
117
|
+
"""Register a callable or on_sdk_event object, optionally for one event type."""
|
|
118
|
+
if not callable(listener) and not hasattr(listener, "on_sdk_event"):
|
|
119
|
+
raise TypeError("event listener must be callable or implement on_sdk_event")
|
|
120
|
+
with self._lock:
|
|
121
|
+
self._listeners.append((listener, event_type))
|
|
122
|
+
|
|
123
|
+
def unregister_listener(self, listener, event_type: str | None = None):
|
|
124
|
+
"""Remove one exact listener registration."""
|
|
125
|
+
with self._lock:
|
|
126
|
+
self._listeners = [(item, subscribed_type)
|
|
127
|
+
for item, subscribed_type in self._listeners
|
|
128
|
+
if item is not listener or subscribed_type != event_type]
|
|
129
|
+
|
|
130
|
+
def publish(self, event: SDKEvent):
|
|
131
|
+
"""Notify matching listeners and isolate their exceptions."""
|
|
132
|
+
with self._lock:
|
|
133
|
+
listeners = list(self._listeners)
|
|
134
|
+
for listener, event_type in listeners:
|
|
135
|
+
if event_type is not None and event_type != event.event_type:
|
|
136
|
+
continue
|
|
137
|
+
try:
|
|
138
|
+
self._invoke(listener, event)
|
|
139
|
+
except Exception as error:
|
|
140
|
+
if self.logger is not None:
|
|
141
|
+
self.logger.warning("event listener %s failed: %s", self._listener_name(listener), error)
|
|
142
|
+
|
|
143
|
+
def close(self):
|
|
144
|
+
"""Drop listeners when the owning context is destroyed."""
|
|
145
|
+
with self._lock:
|
|
146
|
+
self._listeners.clear()
|
|
147
|
+
if self.logger is not None:
|
|
148
|
+
self.logger.info("event center closed")
|
|
149
|
+
|
|
150
|
+
def _log_event(self, event):
|
|
151
|
+
if self.logger is None:
|
|
152
|
+
return
|
|
153
|
+
self.logger.info("%s", event.to_log_message())
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def now_event(client_id: str, event_type: str, event_name: str, **values) -> SDKEvent:
|
|
157
|
+
"""Build a timestamped event for internal emitters."""
|
|
158
|
+
return SDKEvent(event_type=event_type, event_name=event_name, timestamp=int(time.time() * 1000),
|
|
159
|
+
client_id=client_id, **values)
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Tencent is pleased to support the open source community by making polaris-python available.
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2021 Tencent. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the BSD 3-Clause License (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# https://opensource.org/licenses/BSD-3-Clause
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
"""Lossless online/offline models aligned with the Java LosslessPolicy plugin."""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from abc import ABC, abstractmethod
|
|
22
|
+
from dataclasses import dataclass, field
|
|
23
|
+
from enum import Enum
|
|
24
|
+
from typing import Any, Dict
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class RegisterStatus(Enum):
|
|
28
|
+
"""Whether a lossless instance has completed its registration."""
|
|
29
|
+
|
|
30
|
+
UNREGISTERED = "UNREGISTERED"
|
|
31
|
+
REGISTERED = "REGISTERED"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class LosslessInstance:
|
|
36
|
+
"""Identify one provider instance participating in lossless lifecycle."""
|
|
37
|
+
|
|
38
|
+
namespace: str
|
|
39
|
+
service: str
|
|
40
|
+
host: str
|
|
41
|
+
port: int
|
|
42
|
+
metadata: Dict[str, str] = field(default_factory=dict)
|
|
43
|
+
|
|
44
|
+
def __hash__(self):
|
|
45
|
+
return hash((self.namespace, self.service, self.host, self.port))
|
|
46
|
+
|
|
47
|
+
def __eq__(self, other):
|
|
48
|
+
if not isinstance(other, LosslessInstance):
|
|
49
|
+
return NotImplemented
|
|
50
|
+
return (self.namespace, self.service, self.host, self.port) == (
|
|
51
|
+
other.namespace, other.service, other.host, other.port)
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def from_register_request(cls, request) -> "LosslessInstance":
|
|
55
|
+
return cls(
|
|
56
|
+
namespace=request.namespace,
|
|
57
|
+
service=request.service,
|
|
58
|
+
host=request.host,
|
|
59
|
+
port=request.port,
|
|
60
|
+
metadata=dict(getattr(request, "metadata", {}) or {}),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class InstanceProperties:
|
|
65
|
+
"""Properties a lossless policy may set on the instance before registration."""
|
|
66
|
+
|
|
67
|
+
def __init__(self) -> None:
|
|
68
|
+
self._properties: Dict[str, Any] = {}
|
|
69
|
+
|
|
70
|
+
def set_property(self, key: str, value: Any) -> None:
|
|
71
|
+
self._properties[key] = value
|
|
72
|
+
|
|
73
|
+
def get_property(self, key: str) -> Any:
|
|
74
|
+
return self._properties.get(key)
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def properties(self) -> Dict[str, Any]:
|
|
78
|
+
return dict(self._properties)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class LosslessActionProvider(ABC):
|
|
82
|
+
"""Callbacks that perform the real register, deregister and health checks."""
|
|
83
|
+
|
|
84
|
+
@abstractmethod
|
|
85
|
+
def get_name(self) -> str:
|
|
86
|
+
"""Return the provider name used in logs."""
|
|
87
|
+
|
|
88
|
+
@abstractmethod
|
|
89
|
+
async def do_register(self, instance_properties: InstanceProperties):
|
|
90
|
+
"""Register the instance after the lossless-online gate."""
|
|
91
|
+
|
|
92
|
+
@abstractmethod
|
|
93
|
+
async def do_deregister(self):
|
|
94
|
+
"""Deregister the instance when lossless offline is triggered."""
|
|
95
|
+
|
|
96
|
+
@abstractmethod
|
|
97
|
+
def is_enable_health_check(self) -> bool:
|
|
98
|
+
"""Return True to register after a successful health check."""
|
|
99
|
+
|
|
100
|
+
@abstractmethod
|
|
101
|
+
async def do_health_check(self) -> bool:
|
|
102
|
+
"""Return True when the instance is ready to receive traffic."""
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Tencent is pleased to support the open source community by making polaris-python available.
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2021 Tencent. All rights reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the BSD 3-Clause License (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# https://opensource.org/licenses/BSD-3-Clause
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software distributed
|
|
12
|
+
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
13
|
+
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
14
|
+
# specific language governing permissions and limitations under the License.
|
|
15
|
+
|
|
16
|
+
from polaris.pkg.model.core import EventType
|
|
17
|
+
from polarismesh_specification.api.v1.service_manage import response_pb2
|
|
18
|
+
from polaris.pkg.model.error import SDKError, ErrorCode
|
|
19
|
+
|
|
20
|
+
from abc import ABCMeta, abstractmethod
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ServiceRule 服务配置通用接口
|
|
24
|
+
class ServiceRule(metaclass=ABCMeta):
|
|
25
|
+
@abstractmethod
|
|
26
|
+
def get_type(self):
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def get_value(self):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
@abstractmethod
|
|
34
|
+
def is_initialized(self):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def get_revision(self):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# 通用的服务规则实现
|
|
43
|
+
class SimpleServiceRule(ServiceRule):
|
|
44
|
+
def __init(self, event_type: EventType, value, initialized: bool, revision: str):
|
|
45
|
+
self.event_type = event_type
|
|
46
|
+
self.value = value
|
|
47
|
+
self.initialized = initialized
|
|
48
|
+
self.revision = revision
|
|
49
|
+
|
|
50
|
+
def get_type(self):
|
|
51
|
+
return self.event_type
|
|
52
|
+
|
|
53
|
+
def get_value(self):
|
|
54
|
+
return self.value
|
|
55
|
+
|
|
56
|
+
def is_initialized(self):
|
|
57
|
+
return self.initialized
|
|
58
|
+
|
|
59
|
+
def get_revision(self):
|
|
60
|
+
return self.revision
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# 创建通用的服务规则
|
|
64
|
+
def new_simple_service_rule(event_type: EventType, value, revision: str) -> ServiceRule:
|
|
65
|
+
svc_rule = SimpleServiceRule()
|
|
66
|
+
svc_rule.event_type = event_type
|
|
67
|
+
if value is None:
|
|
68
|
+
svc_rule.initialized = False
|
|
69
|
+
return svc_rule
|
|
70
|
+
svc_rule.initialized = True
|
|
71
|
+
svc_rule.value = value
|
|
72
|
+
svc_rule.revision = revision
|
|
73
|
+
return svc_rule
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# 获取服务规则请求
|
|
77
|
+
class GetServiceRuleRequest:
|
|
78
|
+
def __init__(self, flow_id: int = 0, namespace: str = "", service: str = "", timeout: int = 0,
|
|
79
|
+
retry_count: int = 0):
|
|
80
|
+
# 可选,流水号,用于跟踪用户的请求,默认0
|
|
81
|
+
self.flow_id = flow_id
|
|
82
|
+
# 命名空间
|
|
83
|
+
self.namespace = namespace
|
|
84
|
+
# 服务名
|
|
85
|
+
self.service = service
|
|
86
|
+
# 可选,仅对同步请求有效,本次查询最大超时信息,可选,默认直接获取全局的超时配置
|
|
87
|
+
self.timeout = timeout
|
|
88
|
+
# 可选,重试次数,默认直接获取全局的超时配置
|
|
89
|
+
self.retry_count = retry_count
|
|
90
|
+
|
|
91
|
+
def get_service(self):
|
|
92
|
+
return self.service
|
|
93
|
+
|
|
94
|
+
def get_namespace(self):
|
|
95
|
+
return self.namespace
|
|
96
|
+
|
|
97
|
+
def get_metadata(self):
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
def set_timeout(self, duration: int):
|
|
101
|
+
self.timeout = duration
|
|
102
|
+
|
|
103
|
+
def set_retry_count(self, retry_count: int):
|
|
104
|
+
self.retry_count = retry_count
|
|
105
|
+
|
|
106
|
+
def validate(self):
|
|
107
|
+
validate_service_metadata(self)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# 服务规则应答
|
|
111
|
+
class ServiceRuleResponse(ServiceRule):
|
|
112
|
+
def __init__(self, event_type: EventType, value, revision: str):
|
|
113
|
+
self.event_type = event_type
|
|
114
|
+
self.value = value
|
|
115
|
+
self.revision = revision
|
|
116
|
+
|
|
117
|
+
def get_type(self):
|
|
118
|
+
return self.event_type
|
|
119
|
+
|
|
120
|
+
def get_value(self):
|
|
121
|
+
return self.value
|
|
122
|
+
|
|
123
|
+
def is_initialized(self):
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
def get_revision(self):
|
|
127
|
+
return self.revision
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class RoutingRuleInProto:
|
|
131
|
+
@classmethod
|
|
132
|
+
def new_from_discover_rsp(cls, resp: response_pb2.DiscoverResponse) -> ServiceRule:
|
|
133
|
+
if resp is None:
|
|
134
|
+
return new_simple_service_rule(event_type=EventType.Routing, value=None, revision="")
|
|
135
|
+
elif resp.routing is None:
|
|
136
|
+
return new_simple_service_rule(event_type=EventType.Routing, value=None, revision="")
|
|
137
|
+
else:
|
|
138
|
+
return new_simple_service_rule(event_type=EventType.Routing, value=resp.routing,
|
|
139
|
+
revision=str(resp.routing.revision.value))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def validate_service_metadata(svc_meta):
|
|
143
|
+
if len(svc_meta.get_service()) == 0:
|
|
144
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument, "validate_service_metadata: service is empty")
|
|
145
|
+
if len(svc_meta.get_namespace()) == 0:
|
|
146
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument, "validate_service_metadata: namespace is empty")
|
|
147
|
+
metadata = svc_meta.get_metadata()
|
|
148
|
+
if metadata is not None and len(metadata) > 0:
|
|
149
|
+
for k, v in svc_meta.get_metadata():
|
|
150
|
+
if len(k) == 0 or len(v) == 0:
|
|
151
|
+
raise SDKError(ErrorCode.ErrCodeAPIInvalidArgument,
|
|
152
|
+
"validate_service_metadata: metadata has empty key or value")
|