-
Notifications
You must be signed in to change notification settings - Fork 0
<fix>[testlib]: rewrite async poll URL to use client-configured hostname #3666
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 5.5.12
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,7 +18,6 @@ | |
| import hmac | ||
| from hashlib import sha1 | ||
| import datetime | ||
| import time | ||
|
|
||
| try: | ||
| int_types = (int, long) | ||
|
|
@@ -58,7 +57,7 @@ def _exception_safe(func): | |
| def wrap(*args, **kwargs): | ||
| try: | ||
| func(*args, **kwargs) | ||
| except: | ||
| except Exception: | ||
| print(traceback.format_exc()) | ||
|
|
||
| return wrap | ||
|
|
@@ -80,6 +79,7 @@ def _http_error(status, body=None): | |
| def _error(code, desc, details): | ||
| err = ErrorCode() | ||
| err.code = code | ||
| err.description = desc | ||
| err.desc = desc | ||
| err.details = details | ||
| return {'error': err} | ||
|
|
@@ -181,7 +181,7 @@ def _check_params(self): | |
| if value is not None and isinstance(value, str) and annotation.max_length and len(value) > annotation.max_length: | ||
| raise SdkError('invalid length[%s] of the parameter[%s], the max allowed length is %s' % (len(value), param_name, annotation.max_length)) | ||
|
|
||
| if value is not None and isinstance(value, str) and annotation.min_length and len(value) > annotation.min_length: | ||
| if value is not None and isinstance(value, str) and annotation.min_length and len(value) < annotation.min_length: | ||
| raise SdkError('invalid length[%s] of the parameter[%s], the minimal allowed length is %s' % (len(value), param_name, annotation.min_length)) | ||
|
|
||
| if value is not None and isinstance(value, list) and annotation.non_empty is True and len(value) == 0: | ||
|
|
@@ -235,11 +235,11 @@ def _url(self): | |
| elements.append('/v1') | ||
|
|
||
| path = self.PATH.replace('{', '${') | ||
| unresolved = re.findall('${(.+?)}', path) | ||
| unresolved = re.findall(r'\$\{(.+?)\}', path) | ||
| params = self._params() | ||
| if unresolved: | ||
| for u in unresolved: | ||
| if u in params: | ||
| if u not in params: | ||
| raise SdkError('missing a mandatory parameter[%s]' % u) | ||
|
|
||
| path = string.Template(path).substitute(params) | ||
|
|
@@ -362,7 +362,21 @@ def _poll_result(self, rsp, cb): | |
| m = json.loads(rsp.data) | ||
| location = m[LOCATION] | ||
| if not location: | ||
| raise SdkError("Internal Error] the api[%s] is an async API but the server doesn't return the polling location url") | ||
| raise SdkError("[Internal Error] the api[%s] is an async API but the server doesn't return the polling location url" % self.PATH) | ||
|
|
||
| # Rewrite poll URL to use client-configured hostname:port, | ||
| # in case server returns an internal IP unreachable from client | ||
| try: | ||
| from urllib.parse import urlparse, urlunparse | ||
| except ImportError: | ||
| from urlparse import urlparse, urlunparse | ||
| parsed = urlparse(location) | ||
| configured_host = __config__[CONFIG_HOSTNAME] | ||
| configured_port = str(__config__[CONFIG_PORT]) | ||
| if ':' in configured_host and not configured_host.startswith('['): | ||
| configured_host = '[%s]' % configured_host | ||
| location = urlunparse(parsed._replace( | ||
| netloc='%s:%s' % (configured_host, configured_port))) | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
+373
to
+379
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
python - <<'PY'
try:
from urllib.parse import urlparse, urlunparse
except ImportError:
from urlparse import urlparse, urlunparse
location = '/v1/api-jobs/uuid'
parsed = urlparse(location)
rewritten = urlunparse(parsed._replace(netloc='127.0.0.1:8080'))
print('input =', location)
print('rewritten=', rewritten)
print('scheme =', urlparse(rewritten).scheme or '<empty>')
PYRepository: MatheMatrix/zstack Length of output: 154 🏁 Script executed: find . -name "zssdk.py" -type fRepository: MatheMatrix/zstack Length of output: 99 🏁 Script executed: sed -n '370,385p' ./testlib/src/main/resources/zssdk.py | cat -nRepository: MatheMatrix/zstack Length of output: 829 🏁 Script executed: # 检查更大的上下文,找到函数定义
sed -n '360,390p' ./testlib/src/main/resources/zssdk.py | cat -nRepository: MatheMatrix/zstack Length of output: 1621 🏁 Script executed: # 检查整个文件是否有中文字符
grep -n '[^\x00-\x7F]' ./testlib/src/main/resources/zssdk.py | head -20Repository: MatheMatrix/zstack Length of output: 472 🏁 Script executed: python3 << 'EOF'
import re
with open('./testlib/src/main/resources/zssdk.py', 'r', encoding='utf-8', errors='ignore') as f:
lines = f.readlines()
for i, line in enumerate(lines, 1):
if any(ord(c) > 127 for c in line):
print(f"Line {i}: {line.rstrip()}")
EOFRepository: MatheMatrix/zstack Length of output: 44 🏁 Script executed: python3 << 'EOF'
try:
from urllib.parse import urlparse, urlunparse
except ImportError:
from urlparse import urlparse, urlunparse
# Test case 1: relative path
location = '/v1/api-jobs/uuid'
parsed = urlparse(location)
# Current behavior (wrong)
rewritten_old = urlunparse(parsed._replace(netloc='127.0.0.1:8080'))
print("Test 1: Relative path /v1/api-jobs/uuid")
print(f" Current (wrong): {rewritten_old}")
print(f" Scheme: {urlparse(rewritten_old).scheme or '<empty>'}")
# Proposed fix
scheme = parsed.scheme or 'http'
rewritten_new = urlunparse(parsed._replace(
scheme=scheme,
netloc='127.0.0.1:8080'))
print(f" Proposed (fixed): {rewritten_new}")
print(f" Scheme: {urlparse(rewritten_new).scheme or '<empty>'}")
# Test case 2: absolute URL with scheme
print("\nTest 2: Absolute URL with scheme")
location2 = 'http://internal-server:8080/v1/api-jobs/uuid'
parsed2 = urlparse(location2)
scheme2 = parsed2.scheme or 'http'
rewritten2 = urlunparse(parsed2._replace(
scheme=scheme2,
netloc='127.0.0.1:8080'))
print(f" Input: {location2}")
print(f" Output: {rewritten2}")
# Test case 3: absolute URL with https
print("\nTest 3: Absolute URL with https")
location3 = 'https://internal-server:8080/v1/api-jobs/uuid'
parsed3 = urlparse(location3)
scheme3 = parsed3.scheme or 'http'
rewritten3 = urlunparse(parsed3._replace(
scheme=scheme3,
netloc='127.0.0.1:8080'))
print(f" Input: {location3}")
print(f" Output: {rewritten3}")
EOFRepository: MatheMatrix/zstack Length of output: 552 相对 当前只替换 建议修改 parsed = urlparse(location)
configured_host = __config__[CONFIG_HOSTNAME]
configured_port = str(__config__[CONFIG_PORT])
if ':' in configured_host and not configured_host.startswith('['):
configured_host = '[%s]' % configured_host
+ scheme = parsed.scheme or 'http'
location = urlunparse(parsed._replace(
+ scheme=scheme,
netloc='%s:%s' % (configured_host, configured_port)))🤖 Prompt for AI Agents |
||
|
|
||
| if cb: | ||
| # async polling | ||
|
|
@@ -480,11 +494,13 @@ def _uuid(): | |
| def _json_http( | ||
| uri, | ||
| body=None, | ||
| headers={}, | ||
| headers=None, | ||
| method='POST', | ||
| timeout=120.0 | ||
| ): | ||
| pool = urllib3.PoolManager(timeout=timeout, retries=urllib3.util.retry.Retry(15)) | ||
| if headers is None: | ||
| headers = {} | ||
| headers.update({'Content-Type': 'application/json', 'Connection': 'close'}) | ||
|
|
||
| if body is not None and not isinstance(body, str): | ||
|
|
@@ -497,6 +513,15 @@ def _json_http( | |
| else: | ||
| rsp = pool.request(method, uri, headers=headers) | ||
|
|
||
| print('[Response to %s %s]: status: %s, body: %s' % (method, uri, rsp.status, rsp.data)) | ||
| return rsp | ||
| data = rsp.data | ||
| if isinstance(data, bytes): | ||
| data = data.decode('utf-8') | ||
| print('[Response to %s %s]: status: %s, body: %s' % (method, uri, rsp.status, data)) | ||
|
|
||
| class _Rsp(object): | ||
| pass | ||
| r = _Rsp() | ||
| r.status = rsp.status | ||
| r.data = data | ||
| return r | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.