test(qa): verify native Kingbase restore migration and fusion workflows

This commit is contained in:
wangzhibiao committed 2026-10-14 18:31:38 +08:00
1 parent ba5dc7a6af
commit 6e73d34561
23 files changed
+1103 -31

No files matched your search

+40 -3
View File
@@ -9,10 +9,12 @@ from pathlib import Path
import secrets
import shlex
import signal
import shutil
import socket
import stat
import subprocess
import sys
import time
import urllib.parse
import xml.etree.ElementTree as ET
@@ -95,6 +97,15 @@ def available(port):
probe.bind(('127.0.0.1', port))
def druid_url(url):
# Druid 1.2.28 also parses URL timeout values as pool milliseconds, whereas
# Kingbase JDBC uses seconds. Keep pool timeout configuration in one place.
parsed = urllib.parse.urlsplit(url.removeprefix('jdbc:'))
query = [(k,v) for k,v in urllib.parse.parse_qsl(parsed.query)
if k.lower() not in ('connecttimeout', 'sockettimeout')]
return 'jdbc:' + urllib.parse.urlunsplit(parsed._replace(query=urllib.parse.urlencode(query)))
def launch(name, command, env):
private_directory()
ledger = RUNTIME / (name + '.json')
@@ -114,6 +125,15 @@ def start(config, env, backend_port, redis_port):
available(backend_port)
available(redis_port)
private_directory()
built_jar = ROOT / 'RuoYi-Vue/ruoyi-admin/target/ruoyi-admin.jar'
if not built_jar.is_file():
raise ValueError('Build backend jar first')
digest = hashlib.sha256(built_jar.read_bytes()).hexdigest()
runtime_jar = RUNTIME / ('ruoyi-admin-' + digest[:16] + '.jar')
if not runtime_jar.exists():
shutil.copyfile(built_jar, runtime_jar)
if hashlib.sha256(runtime_jar.read_bytes()).hexdigest() != digest:
raise ValueError('Runtime artifact hash mismatch')
secret_path = RUNTIME / 'runtime-secrets.env'
if not secret_path.exists():
with open(secret_path, 'x', opener=lambda p, f: os.open(p, f, 0o600)) as f:
@@ -125,17 +145,23 @@ def start(config, env, backend_port, redis_port):
if not config.get(key):
raise ValueError('Missing required protected identity configuration')
env[key] = config[key]
env.update(SPRING_DATASOURCE_DRUID_MASTER_URL=env['QA_DB_URL'],
runtime_url = druid_url(env['QA_DB_URL'])
env.update(SPRING_DATASOURCE_DRUID_MASTER_URL=runtime_url,
SPRING_DATASOURCE_DRUID_MASTER_USERNAME='fusion_qa_app',
SPRING_DATASOURCE_DRUID_MASTER_PASSWORD=env['QA_DB_PASSWORD'],
SPRING_DATA_REDIS_PASSWORD=env['QA_REDIS_PASSWORD'])
redis_config = RUNTIME / 'redis.conf'
with open(redis_config, 'w', opener=lambda p, f: os.open(p, f, 0o600)) as f:
f.write('bind 127.0.0.1\nprotected-mode yes\nport ' + str(redis_port) + '\nsave ""\nappendonly no\nrequirepass ' + env['QA_REDIS_PASSWORD'] + '\n')
command = ['java', '-jar', str(ROOT / 'RuoYi-Vue/ruoyi-admin/target/ruoyi-admin.jar'),
command = ['java', '-jar', str(runtime_jar),
'--spring.profiles.active=druid',
'--spring.datasource.druid.master.url=' + env['QA_DB_URL'],
'--spring.datasource.druid.master.url=' + runtime_url,
'--spring.datasource.druid.master.username=fusion_qa_app',
'--spring.datasource.druid.master.connect-timeout=30000',
'--spring.datasource.druid.master.socket-timeout=60000',
'--spring.datasource.druid.master.initial-size=2',
'--spring.datasource.druid.master.min-idle=2',
'--spring.datasource.druid.master.max-active=8',
'--server.address=127.0.0.1', '--server.port=' + str(backend_port),
'--spring.data.redis.host=127.0.0.1', '--spring.data.redis.port=' + str(redis_port),
'--spring.data.redis.database=0', '--spring.quartz.auto-startup=false',
@@ -153,6 +179,16 @@ def start(config, env, backend_port, redis_port):
launch('backend', command, env)
def wait_for_exit(pid):
deadline = time.monotonic() + 15
while time.monotonic() < deadline:
state = subprocess.run(['ps', '-p', str(pid), '-o', 'stat='], text=True, capture_output=True).stdout.strip()
if not state or state.startswith('Z'):
return
time.sleep(0.1)
raise ValueError('Task-owned process did not stop; ledger retained, no forced kill')
def stop():
private_directory()
for name in ['frontend', 'backend', 'redis']:
@@ -174,6 +210,7 @@ def stop():
if not identity or ('n' + str(ROOT)) not in cwd.splitlines() or (item.get('started') and item['started'] != started):
raise ValueError('PID identity mismatch; refusing to signal ' + name)
os.kill(item['pid'], signal.SIGTERM)
wait_for_exit(item['pid'])
path.unlink()
print('Stopped task-owned ' + name)