Skip to content

Commit a05f402

Browse files
author
yuguo.dtpe
committed
feature: add standard resolver
1 parent cbb2069 commit a05f402

File tree

4 files changed

+366
-3
lines changed

4 files changed

+366
-3
lines changed

SDK_Integration_zh.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- [自定义RegionId](#自定义regionid)
1414
- [自动化Endpoint寻址](#自动化endpoint寻址)
1515
- [Endpoint默认寻址](#endpoint默认寻址)
16+
- [Endpoint标准寻址](#endpoint标准寻址)
1617
- [Http连接池配置](#http连接池配置)
1718
- [Https请求配置](#https请求配置)
1819
- [指定scheme](#指定scheme)
@@ -415,6 +416,33 @@ configuration.custom_bootstrap_region = {
415416
volcenginesdkcore.Configuration.set_default(configuration)
416417
```
417418

419+
### Endpoint标准寻址
420+
**标准寻址规则**
421+
422+
| Global服务 | 双栈 | 格式 |
423+
|----------|----|------------------------------------------------------------------------------------------------------------------|
424+
||| `{Service}.volcengine-api.com` |
425+
||| `{Service}.volcengineapi.com` |
426+
||| `{Service}.{region}.volcengine-api.com`|
427+
||| `{Service}.{region}.volcengineapi.com` |
428+
429+
**代码示例:**
430+
431+
是否global服务根据具体调用的服务决定的,是否global无法修改的。
432+
可以参考列表:[./volcenginesdkcore/endpoint/providers/standard_provider.py#ServiceInfos](./volcenginesdkcore/endpoint/providers/standard_provider.py#L51)
433+
```python
434+
import volcenginesdkcore
435+
from volcenginesdkcore.endpoint.providers.standard_provider import StandardEndpointResolver
436+
configuration = volcenginesdkcore.Configuration()
437+
configuration.ak = "Your ak"
438+
configuration.sk = "Your sk"
439+
configuration.endpoint_provider = StandardEndpointResolver() # 配置标准寻址
440+
configuration.use_dual_stack = True # 配置是否双栈
441+
configuration.region = "cn-beijing" # 配置region
442+
volcenginesdkcore.Configuration.set_default(configuration)
443+
```
444+
445+
418446
# Http连接池配置
419447

420448
> **默认**
Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
# coding=utf-8
2+
from __future__ import unicode_literals, print_function
3+
import re
4+
from volcenginesdkcore.endpoint.endpoint_provider import EndpointProvider, ResolvedEndpoint
5+
6+
7+
# -----------------------------
8+
# Errors (模仿 volcengineerr.New)
9+
# -----------------------------
10+
class StandProviderError(Exception):
11+
def __init__(self, code, message):
12+
Exception.__init__(self, "%s: %s" % (code, message))
13+
self.code = code
14+
self.message = message
15+
16+
17+
# -----------------------------
18+
# 常量
19+
# -----------------------------
20+
DEFAULT_FORMAT = "{Service}{Region}.{SiteStack}.com"
21+
22+
# 站点栈
23+
SITE_STACK_VOLC_IPV4 = "volcengineapi"
24+
SITE_STACK_VOLC_DUAL_STACK = "volcengine-api"
25+
26+
27+
# -----------------------------
28+
# 数据模型(兼容 2.7/3.x 的朴素类)
29+
# -----------------------------
30+
class StandardEndpointResolverVariable(object):
31+
def __init__(self):
32+
self.service = ""
33+
self.region = ""
34+
self.site_stack = ""
35+
self.extension = {}
36+
37+
def to_dict(self):
38+
return {
39+
"Service": self.service,
40+
"Region": self.region,
41+
"SiteStack": self.site_stack,
42+
"Extension": self.extension,
43+
}
44+
45+
46+
class ServiceInfo(object):
47+
def __init__(self, service, is_global):
48+
self.service = service
49+
self.IsGlobal = is_global
50+
51+
ServiceInfos = {
52+
"vpc": ServiceInfo("vpc", False),
53+
"ecs": ServiceInfo("ecs", False),
54+
"billing": ServiceInfo("billing", True),
55+
"ark": ServiceInfo("ark", False),
56+
"iam": ServiceInfo("iam", True),
57+
"mcs": ServiceInfo("mcs", False),
58+
"rocketmq": ServiceInfo("rocketmq", False),
59+
"bytehouse": ServiceInfo("bytehouse", False),
60+
"dns": ServiceInfo("dns", True),
61+
"autoscaling": ServiceInfo("autoscaling", False),
62+
"spark": ServiceInfo("spark", False),
63+
"cloud_detect": ServiceInfo("cloud_detect", False),
64+
"filenas": ServiceInfo("filenas", False),
65+
"escloud": ServiceInfo("escloud", False),
66+
"flink": ServiceInfo("flink", False),
67+
"cp": ServiceInfo("cp", False),
68+
"vefaas": ServiceInfo("vefaas", False),
69+
"ml_platform": ServiceInfo("ml_platform", False),
70+
"edx": ServiceInfo("edx", True),
71+
"dcdn": ServiceInfo("dcdn", True),
72+
"cdn": ServiceInfo("cdn", True),
73+
"kafka": ServiceInfo("kafka", False),
74+
"certificate_service": ServiceInfo("certificate_service", True),
75+
"waf": ServiceInfo("waf", True),
76+
"rds_mssql": ServiceInfo("rds_mssql", False),
77+
"cloudtrail": ServiceInfo("cloudtrail", False),
78+
"vei_api": ServiceInfo("vei_api", True),
79+
"cen": ServiceInfo("cen", True),
80+
"rabbitmq": ServiceInfo("rabbitmq", False),
81+
"vmp": ServiceInfo("vmp", False),
82+
"volc_observe": ServiceInfo("volc_observe", False),
83+
"dataleap": ServiceInfo("dataleap", False),
84+
"fw_center": ServiceInfo("fw_center", True),
85+
"redis": ServiceInfo("redis", False),
86+
"mcdn": ServiceInfo("mcdn", True),
87+
"cloudidentity": ServiceInfo("cloudidentity", False),
88+
"vedbm": ServiceInfo("vedbm", False),
89+
"cv": ServiceInfo("cv", True),
90+
"translate": ServiceInfo("translate", True),
91+
"cloud_trail": ServiceInfo("cloud_trail", False),
92+
"bio": ServiceInfo("bio", False),
93+
"nta": ServiceInfo("nta", True),
94+
"elasticmapreduce": ServiceInfo("elasticmapreduce", False),
95+
"vepfs": ServiceInfo("vepfs", False),
96+
"seccenter": ServiceInfo("seccenter", True),
97+
"advdefence": ServiceInfo("advdefence", True),
98+
"tis": ServiceInfo("tis", True),
99+
"organization": ServiceInfo("organization", True),
100+
"vke": ServiceInfo("vke", False),
101+
"Redis": ServiceInfo("Redis", False),
102+
"privatelink": ServiceInfo("privatelink", False),
103+
"RocketMQ": ServiceInfo("RocketMQ", False),
104+
"Kafka": ServiceInfo("Kafka", False),
105+
"rds_mysql": ServiceInfo("rds_mysql", False),
106+
"rds_postgresql": ServiceInfo("rds_postgresql", False),
107+
"storage_ebs": ServiceInfo("storage_ebs", False),
108+
"clb": ServiceInfo("clb", False),
109+
"alb": ServiceInfo("alb", False),
110+
"FileNAS": ServiceInfo("FileNAS", False),
111+
"configcenter": ServiceInfo("configcenter", False),
112+
"cr": ServiceInfo("cr", False),
113+
"sts": ServiceInfo("sts", False),
114+
"mongodb": ServiceInfo("mongodb", False),
115+
"transitrouter": ServiceInfo("transitrouter", False),
116+
"Volc_Observe": ServiceInfo("Volc_Observe", False),
117+
"dms": ServiceInfo("dms", False),
118+
"auto_scaling": ServiceInfo("auto_scaling", False),
119+
"directconnect": ServiceInfo("directconnect", False),
120+
"kms": ServiceInfo("kms", False),
121+
"dbw": ServiceInfo("dbw", False),
122+
"dts": ServiceInfo("dts", False),
123+
"natgateway": ServiceInfo("natgateway", False),
124+
"tos": ServiceInfo("tos", False),
125+
"TLS": ServiceInfo("TLS", False),
126+
"vpn": ServiceInfo("vpn", False),
127+
"vod": ServiceInfo("vod", False),
128+
"quota": ServiceInfo("quota", True),
129+
"ecs_ops": ServiceInfo("ecs_ops", True),
130+
"as_ops": ServiceInfo("as_ops", True),
131+
"account_management": ServiceInfo("account_management", True),
132+
"account_management_byteplus": ServiceInfo("account_management_byteplus", True),
133+
"bandwidthquota": ServiceInfo("bandwidthquota", True),
134+
"psa_manager": ServiceInfo("psa_manager", True),
135+
"dc_controller": ServiceInfo("dc_controller", False),
136+
"eps_platform_trade": ServiceInfo("eps_platform_trade", False),
137+
"eps_platform_fund": ServiceInfo("eps_platform_fund", False),
138+
"commercialization": ServiceInfo("commercialization", True),
139+
"veecp_openapi": ServiceInfo("veecp_openapi", False),
140+
"orgnization": ServiceInfo("orgnization", True),
141+
"coze": ServiceInfo("coze", True),
142+
"sec_agent": ServiceInfo("sec_agent", True),
143+
"sec_intelligent_dev": ServiceInfo("sec_intelligent_dev", True),
144+
"vegame": ServiceInfo("vegame", False),
145+
"acep": ServiceInfo("acep", True),
146+
"private_zone": ServiceInfo("private_zone", True),
147+
"sqs": ServiceInfo("sqs", False),
148+
"resourcecenter": ServiceInfo("resourcecenter", True),
149+
"aiotvideo": ServiceInfo("aiotvideo", True),
150+
"apig": ServiceInfo("apig", False),
151+
"bmq": ServiceInfo("bmq", False),
152+
"bytehouse_ce": ServiceInfo("bytehouse_ce", False),
153+
"cloudmonitor": ServiceInfo("cloudmonitor", False),
154+
"emr": ServiceInfo("emr", False),
155+
"ga": ServiceInfo("ga", True),
156+
"graph": ServiceInfo("graph", False),
157+
"gtm": ServiceInfo("gtm", True),
158+
"hbase": ServiceInfo("hbase", False),
159+
"metakms": ServiceInfo("metakms", False),
160+
"na": ServiceInfo("na", True),
161+
"resource_share": ServiceInfo("resource_share", True),
162+
"speech_saas_prod": ServiceInfo("speech_saas_prod", True),
163+
"tag": ServiceInfo("tag", True),
164+
"vefaas_dev": ServiceInfo("vefaas_dev", False),
165+
"vms": ServiceInfo("vms", False),
166+
"eco_partner": ServiceInfo("eco_partner", True),
167+
"smc": ServiceInfo("smc", True),
168+
}
169+
170+
171+
# -----------------------------
172+
# Region 校验
173+
# -----------------------------
174+
class RegionChecker(object):
175+
def __init__(self, white_regions, pattern):
176+
self.white_regions = white_regions or {}
177+
self.pattern = pattern
178+
179+
def validate(self, region):
180+
if self.white_regions and region in self.white_regions:
181+
return True
182+
if self.pattern:
183+
return bool(self.pattern.match(region))
184+
return False
185+
186+
187+
region_matcher = RegionChecker(
188+
{
189+
"ap-singapore-1": {},
190+
"ap-southeast-1": {},
191+
"ap-southeast-2": {},
192+
"ap-southeast-3": {},
193+
"byteplus-global": {},
194+
"cn-beijing": {},
195+
"cn-beijing-autodriving": {},
196+
"cn-beijing-selfdrive": {},
197+
"cn-beijing2": {},
198+
"cn-beijing300": {},
199+
"cn-changsha-sdv": {},
200+
"cn-chengdu": {},
201+
"cn-chengdu-sdv": {},
202+
"cn-chongqing-sdv": {},
203+
"cn-datong": {},
204+
"cn-east-1-dedicated": {},
205+
"cn-gaofang-bj": {},
206+
"cn-gaofang-gz1": {},
207+
"cn-gaofang-nt1": {},
208+
"cn-gaofang-nt2": {},
209+
"cn-gaofang-nt3": {},
210+
"cn-gaofang-nt4": {},
211+
"cn-gaofang-nt5": {},
212+
"cn-guangzhou": {},
213+
"cn-guilin-boe": {},
214+
"cn-hangzhou": {},
215+
"cn-hjxj": {},
216+
"cn-hjzg": {},
217+
"cn-hlbx": {},
218+
"cn-hlxj": {},
219+
"cn-hlzg": {},
220+
"cn-hongkong": {},
221+
"cn-hongkong-pop": {},
222+
"cn-lfbx": {},
223+
"cn-lfxj": {},
224+
"cn-lfzg": {},
225+
"cn-macau-pop-sdv": {},
226+
"cn-mainland": {},
227+
"cn-nanjing-bbit": {},
228+
"cn-ningbo-sdv": {},
229+
"cn-north-1": {},
230+
"cn-north-1-dedicated": {},
231+
"cn-north-boe": {},
232+
"cn-shanghai": {},
233+
"cn-shanghai-autodriving": {},
234+
"cn-taiwan-boe": {},
235+
"cn-wuhan": {},
236+
"cn-wulanchabu": {},
237+
"cn-xian-boe-sdv": {},
238+
"overseas-1": {},
239+
"rec-cn": {},
240+
"rec-sg": {},
241+
},
242+
re.compile(
243+
r"^(?:[a-z]{2}-[a-z]+(?:-[a-z]+)?|(?:cn|ap|eu|na|sa|me|af)-[a-z]+-\d+(?:-(?:finance|exclusive|local|inner))?)$"
244+
),
245+
)
246+
247+
248+
# -----------------------------
249+
# 工具函数
250+
# -----------------------------
251+
def standardize_domain_service_code(service_code):
252+
"""lower + '_' -> '-'"""
253+
return service_code.lower().replace("_", "-")
254+
255+
256+
# -----------------------------
257+
# 解析器
258+
# -----------------------------
259+
class StandardEndpointResolver(EndpointProvider):
260+
def __init__(self, fmt=None, site_stack=None, extension=None, custom_services=None):
261+
self.fmt = fmt or DEFAULT_FORMAT
262+
self.variables = StandardEndpointResolverVariable()
263+
self.variables.site_stack = site_stack or SITE_STACK_VOLC_IPV4
264+
self.variables.extension = extension or {}
265+
self.custom_services = custom_services or {}
266+
267+
def endpoint_for(self, service, region, custom_bootstrap_region=None, use_dual_stack=None, **kwargs):
268+
# 1) Region 校验
269+
if not region_matcher.validate(region):
270+
raise StandProviderError(
271+
"InvalidRegion",
272+
"invalid region %s for standard endpoint resolver, "
273+
"please upgrade the sdk endpoint resolver to the latest version" % region
274+
)
275+
276+
# 2) 默认模板
277+
fmt = self.fmt or DEFAULT_FORMAT
278+
279+
# 3) 变量准备
280+
self.variables.service = standardize_domain_service_code(service)
281+
282+
svc_info = ServiceInfos.get(service)
283+
if not svc_info:
284+
svc_info = self.custom_services.get(service)
285+
if not svc_info:
286+
raise StandProviderError(
287+
"ServiceNotFound",
288+
"service %s not found in ServiceInfos or customServices, "
289+
"please upgrade the sdk endpoint resolver to the latest version" % service
290+
)
291+
292+
# 非全局服务拼接 ".{region}",全局则为空
293+
if not svc_info.IsGlobal:
294+
self.variables.region = "." + region
295+
else:
296+
self.variables.region = ""
297+
298+
# 4) IP 版本 -> SiteStack
299+
if use_dual_stack:
300+
self.variables.site_stack = SITE_STACK_VOLC_DUAL_STACK
301+
else:
302+
self.variables.site_stack = SITE_STACK_VOLC_IPV4
303+
304+
# 5) 渲染
305+
try:
306+
rendered = fmt.format(**self.variables.to_dict())
307+
except KeyError as err:
308+
raise StandProviderError(
309+
"TemplateExecuteError",
310+
"failed to execute template for format %s, missing variable %s" % (fmt, err)
311+
)
312+
except Exception as err:
313+
raise StandProviderError(
314+
"TemplateExecuteError",
315+
"failed to execute template for format %s, variable %r: %s" % (fmt, self.variables.to_dict(), err)
316+
)
317+
318+
return ResolvedEndpoint(rendered)
319+
320+
321+
# -----------------------------
322+
if __name__ == "__main__":
323+
r = StandardEndpointResolver()
324+
try:
325+
ep = r.endpoint_for(
326+
service="vpc",
327+
region="ap-southeast-1",
328+
use_dual_stack=False,
329+
)
330+
print(ep.host) # 例如:vpc.ap-southeast-1.volcengine-api.com
331+
except Exception as e:
332+
e.print()

volcenginesdkcore/interceptor/interceptors/resolve_endpoint_interceptor.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .interceptor import RequestInterceptor
2+
from ...observability.debugger import sdk_core_logger
23

34

45
class ResolveEndpointInterceptor(RequestInterceptor):
@@ -20,5 +21,7 @@ def intercept(self, context):
2021
else:
2122
prefix = scheme + '://' + host
2223
context.request.url = prefix + context.request.true_path
23-
24+
sdk_core_logger.debug_endpoint(
25+
"Using endpoint: %s", context.request.host
26+
)
2427
return context

0 commit comments

Comments
 (0)