#!/usr/bin/env python3 """Restore the user-supplied dump only to the new isolated MySQL container.""" import json, os, subprocess, sys, time from pathlib import Path os.umask(0o077) ROOT=Path('/home/ubuntu/apps/syly2-uat') DC=['docker','compose','-f',str(ROOT/'compose.json')] cid=subprocess.check_output(DC+['ps','-q','db'],text=True).strip() assert cid label=subprocess.check_output(['docker','inspect','--format','{{index .Config.Labels "com.docker.compose.project"}}',cid],text=True).strip() assert label=='syly2-uat' phase=sys.argv[1] cmd=['docker','exec','-i',cid,'/opt/mysql/bin/mysql','--max-allowed-packet=512M','--default-character-set=utf8mb4','-uroot','-N','-B'] if phase=='inventory': cmd.insert(-4,'--defaults-extra-file=/run/secrets/mysql-client.cnf') def query(sql): p=subprocess.run(cmd,input=sql,text=True,capture_output=True) if p.returncode: (ROOT/'evidence/db-last-error.log').write_text(p.stderr) raise RuntimeError('SQL failed; private error log saved') return p.stdout.strip() if phase=='restore': assert not (ROOT/'evidence/restore-complete.json').exists(), 'Refusing repeated restore over existing UAT' assert query("SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name='sanya_ch'")=='0' started=time.time() with (ROOT/'incoming/sanya_ch_full.sql').open('rb') as inp,(ROOT/'evidence/restore.log').open('wb') as log: p=subprocess.run(cmd,stdin=inp,stdout=log,stderr=log) if p.returncode: raise RuntimeError('Restore failed; inspect private log') tables=query("SELECT table_name FROM information_schema.tables WHERE table_schema='sanya_ch' AND table_type='BASE TABLE' ORDER BY table_name").splitlines() counts={t:int(query('SELECT COUNT(*) FROM sanya_ch.`'+t+'`')) for t in tables} info={'mysql_version':query('SELECT VERSION()'),'tables':len(tables),'rows':sum(counts.values()),'row_counts':counts,'seconds':round(time.time()-started,1)} (ROOT/'evidence/restore-complete.json').write_text(json.dumps(info,indent=2)) cols=query("SELECT column_name FROM information_schema.columns WHERE table_schema='sanya_ch' AND table_name='t_s_base_user'").splitlines() (ROOT/'evidence/base-user-columns.json').write_text(json.dumps(cols)) print(json.dumps({k:v for k,v in info.items() if k!='row_counts'})) elif phase=='seed': assert (ROOT/'evidence/restore-complete.json').exists() assert query("SELECT COUNT(*) FROM sanya_ch.t_s_base_user WHERE username='uat_admin'")=='0', 'Refusing to change an existing account' creds=json.loads((ROOT/'secrets/credentials.json').read_text()) jdk='/home/ubuntu/.local/opt/syly2-build/corretto-8.512.09.1/bin/' helper=ROOT/'scripts/HashUat.java' helper.write_text('import java.io.*; import org.mindrot.jbcrypt.BCrypt; public class HashUat { public static void main(String[] a) throws Exception { String p=new BufferedReader(new InputStreamReader(System.in)).readLine(); System.out.print(BCrypt.hashpw(p,BCrypt.gensalt(12))); }}') cp=str(ROOT/'runtime/webapp/WEB-INF/lib/*') subprocess.run([jdk+'javac','-cp',cp,str(helper)],check=True) hashed=subprocess.check_output([jdk+'java','-cp',str(ROOT/'scripts')+':'+cp,'HashUat'],input=creds['uat_password'],text=True) columns=json.loads((ROOT/'evidence/base-user-columns.json').read_text()) added=[] if 'password_update_date' not in columns: query('ALTER TABLE sanya_ch.t_s_base_user ADD COLUMN password_update_date datetime NULL;') added.append('t_s_base_user.password_update_date') # All IDs belong to this synthetic UAT fixture, never to a restored account. uid='01a186a4e1407e328d2bfe6687e68b01' org='01a186a4e1407e328d2bfe6687e68b02' role='01a186a4e1407e328d2bfe6687e68b03' sql=f"""USE sanya_ch; START TRANSACTION; INSERT INTO t_s_depart(ID,departname,description,org_code,org_type) VALUES('{org}','UAT验证组织','仅用于隔离测试','UAT','1'); INSERT INTO t_s_base_user(ID,username,password,realname,status,activitiSync,area,login_fail_count,is_locked,password_update_date) VALUES('{uid}','uat_admin','{hashed}','UAT验证管理员',1,0,'460200',0,0,NOW()); INSERT INTO t_s_user(id,create_date,create_by,create_name) VALUES('{uid}',NOW(),'uat-deploy','UAT deployment'); INSERT INTO t_s_user_org(ID,user_id,org_id) VALUES(REPLACE(UUID(),'-',''),'{uid}','{org}'); INSERT INTO t_s_role(ID,rolecode,rolename) VALUES('{role}','uat_admin','UAT验证角色'); INSERT INTO t_s_role_user(ID,roleid,userid) VALUES(REPLACE(UUID(),'-',''),'{role}','{uid}'); INSERT INTO t_s_role_function(ID,functionid,roleid) SELECT REPLACE(UUID(),'-',''),ID,'{role}' FROM t_s_function; COMMIT; CREATE USER 'syly_uat'@'%' IDENTIFIED BY '{creds['mysql_app']}'; GRANT SELECT, INSERT, UPDATE, DELETE, EXECUTE, SHOW VIEW ON sanya_ch.* TO 'syly_uat'@'%'; ALTER USER 'root'@'localhost' IDENTIFIED BY '{creds['mysql_root']}'; """ query(sql) (ROOT/'evidence/uat-seed.json').write_text(json.dumps({'username':'uat_admin','user_id':uid,'org_id':org,'role_id':role,'added_columns':added,'existing_account_passwords_changed':False,'business_rows_changed':False},indent=2)) print('Dedicated UAT account added; original accounts preserved; DB credentials initialized.') else: raise ValueError(phase)