Repository source: docs/plans/application_capacity_study/transfer_driver.py.txt
import collector as c
from pathlib import Path
import subprocess,time,socket,urllib.request,urllib.error,tarfile,zipfile,hashlib,shutil
r=c.ROOT
sock=socket.socket();sock.bind(('127.0.0.1',0));port=sock.getsockname()[1];sock.close()
log=open(r/'s4-server.log','wb')
p=subprocess.Popen(['php','-S',f'127.0.0.1:{port}','-t',str(c.APP/'public'),str(r/'router.php')],cwd=c.APP,env=c.ENV,stdout=log,stderr=log)
try:
for _ in range(100):
try:s=socket.create_connection(('127.0.0.1',port),.1);s.close();break
except OSError:time.sleep(.05)
for name in ('repository.tar.gz','repository.zip','images.zip'):
previous=None
for i in range(3):
start=time.monotonic()
try:
with urllib.request.urlopen(f'http://127.0.0.1:{port}/downloads/{name}',timeout=30) as response:data=response.read();status=response.status
except urllib.error.HTTPError as e:data=e.read();status=e.code
print(f'Download: {name}\nRepeat: {i}\nHTTP: {status}\nElapsed seconds: {time.monotonic()-start:.6f}\nBytes: {len(data)}\nSHA256: {hashlib.sha256(data).hexdigest()}\n',flush=True)
if status!=200:raise RuntimeError(data[:500])
if previous is not None:assert previous==data,'warm archive bytes changed'
previous=data;(r/name).write_bytes(data)
expected={str(x.relative_to(r/'repository')):hashlib.sha256(x.read_bytes()).hexdigest() for x in (r/'repository/records').rglob('*') if x.is_file()}
for name in ('repository.tar.gz','repository.zip'):
if name.endswith('.zip'):
with zipfile.ZipFile(r/name) as z:actual={n.removeprefix('repository/'):hashlib.sha256(z.read(n)).hexdigest() for n in z.namelist() if n.startswith('repository/records/') and not n.endswith('/')}
else:
with tarfile.open(r/name) as t:actual={m.name.removeprefix('repository/'):hashlib.sha256(t.extractfile(m).read()).hexdigest() for m in t if m.isfile() and m.name.startswith('repository/records/')}
assert actual==expected,(name,len(actual),len(expected))
print(f'Exact canonical record archive parity: {name}, {len(expected)} files',flush=True)
dest=r/'import-target';shutil.copytree(c.APP/'tests/fixtures/parity_minimal_v1',dest)
for args in (['init','-q'],['config','user.name','Import fixture'],['config','user.email','fixture@example.invalid'],['add','.'],['commit','-qm','initial']):subprocess.run(['git','-C',str(dest),*args],env=c.ENV,check=True)
env=dict(c.ENV);env.update({'FORUM_REPOSITORY_ROOT':str(dest),'FORUM_DATABASE_PATH':str(r/'import.sqlite'),'FORUM_STATIC_HTML_ROOT':str(r/'import-static'),'FORUM_PUBLIC_ARTIFACT_ROOT':str(r/'import-public'),'FORUM_IMAGE_ROOT':str(r/'import-images')})
commands=[['./v3','import-repository',str(r/'repository.tar.gz'),str(dest),str(r/'import.sqlite'),str(r/'import-public'),'--dry-run'],['./v3','import-instance',f'http://127.0.0.1:{port}','--repository-root='+str(dest),'--database-path='+str(r/'import.sqlite'),'--static-html-root='+str(r/'import-static'),'--dry-run'],['./v3','import-instance','--resume','--repository-root='+str(dest),'--database-path='+str(r/'import.sqlite'),'--static-html-root='+str(r/'import-static')]]
for i,cmd in enumerate(commands):
start=time.monotonic()
with (r/f's4-import-{i}.log').open('wb') as out:result=subprocess.run(cmd,cwd=c.APP,env=env,stdout=out,stderr=subprocess.STDOUT,timeout=120)
print(f'Import case: {i}\nCommand: {cmd}\nExit: {result.returncode}\nElapsed seconds: {time.monotonic()-start:.6f}\n',flush=True)
finally:p.terminate();p.wait(timeout=5);log.close()