90 lines
4.6 KiB
Python
90 lines
4.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Remove/purge the tested local package in the dedicated validation VM."""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
|
|
|
|
def sha(path):
|
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--smoke', type=Path, required=True)
|
|
parser.add_argument('--output', type=Path, required=True)
|
|
args = parser.parse_args()
|
|
assert os.getuid() != 0 and Path.home() == Path('/home/docview')
|
|
smoke = json.loads((args.smoke / 'report.json').read_text())
|
|
assert smoke['success'] and smoke['smokeConditions'] == 12
|
|
for name, digest in smoke['executables'].items():
|
|
assert sha(Path('/opt/docview/bin') / name) == digest
|
|
args.output.mkdir(parents=True, exist_ok=False)
|
|
record = {'success': False, 'runnerSha256': sha(Path(__file__)),
|
|
'smokeReportSha256': sha(args.smoke / 'report.json'), 'commands': []}
|
|
|
|
def run(name, command, allowed=(0,)):
|
|
result = subprocess.run(command, capture_output=True, text=True, timeout=180)
|
|
log = args.output / (name + '.log')
|
|
log.write_text(result.stdout + result.stderr)
|
|
record['commands'].append({'name': name, 'command': command, 'exitCode': result.returncode,
|
|
'logSha256': sha(log)})
|
|
assert result.returncode in allowed, name
|
|
return result.stdout
|
|
|
|
def profiles(label):
|
|
return run(label, ['sudo', 'cat', '/sys/kernel/security/apparmor/profiles'])
|
|
|
|
bundled = 'docview-bundled-qtwebengine '
|
|
sentinels = []
|
|
try:
|
|
before = profiles('profiles-installed')
|
|
assert bundled in before and 'docview-qtwebengine ' in before
|
|
record['installedPackage'] = run('package-installed', ['dpkg-query', '-W',
|
|
'-f=${Package} ${Version} ${db:Status-Status} ${Installed-Size}\n', 'docview']).strip()
|
|
assert ' installed ' in record['installedPackage']
|
|
assert run('userns-before', ['sysctl', '-n', 'kernel.apparmor_restrict_unprivileged_userns']).strip() == '1'
|
|
for relative in ['.config/docview', '.local/state/docview', '.local/share/docview',
|
|
'validation/package-user-document']:
|
|
directory = Path.home() / relative
|
|
directory.mkdir(parents=True, exist_ok=True)
|
|
path = directory / 'deb-preservation-probe.txt'
|
|
with path.open('x') as stream:
|
|
stream.write('DocView package removal preservation probe — 日本語\n')
|
|
sentinels.append({'path': str(path), 'sha256': sha(path)})
|
|
run('apt-remove', ['sudo', 'env', 'DEBIAN_FRONTEND=noninteractive', 'apt-get', '-y', 'remove', 'docview'])
|
|
after = profiles('profiles-removed')
|
|
assert bundled not in after and 'docview-qtwebengine ' in after
|
|
for name in ['/opt/docview', '/usr/bin/docview', '/usr/share/applications/docview.desktop']:
|
|
assert not Path(name).exists(), name
|
|
assert Path('/etc/apparmor.d/docview').is_file()
|
|
assert all(sha(Path(row['path'])) == row['sha256'] for row in sentinels)
|
|
record['remove'] = {'payloadRemoved': True, 'profileUnloaded': True,
|
|
'conffilePreserved': True, 'userProbesPreserved': True}
|
|
run('apt-purge', ['sudo', 'env', 'DEBIAN_FRONTEND=noninteractive', 'apt-get', '-y', 'purge', 'docview'])
|
|
assert not Path('/etc/apparmor.d/docview').exists()
|
|
assert all(sha(Path(row['path'])) == row['sha256'] for row in sentinels)
|
|
assert run('userns-after', ['sysctl', '-n', 'kernel.apparmor_restrict_unprivileged_userns']).strip() == '1'
|
|
assert Path('/opt/docview-qt/6.11.2/gcc_64/bin/qtpaths').is_file()
|
|
assert Path('/opt/docview-deps/lib/libqpdf.so.30').is_file()
|
|
assert Path('/home/docview/docview-install/bin/docview').is_file()
|
|
assert Path('/home/docview/docview-build/docview').is_file()
|
|
record['purge'] = {'conffileRemoved': True, 'userProbesPreserved': True}
|
|
record.update(success=True, userProbes=sentinels, originalDevelopmentPrefixesRestored=True,
|
|
kernelUserNamespaceRestrictionUnchanged=True)
|
|
finally:
|
|
# Delete only these newly-created probes after preserving their hashes.
|
|
for row in sentinels:
|
|
path = Path(row['path'])
|
|
if path.is_file() and sha(path) == row['sha256']:
|
|
path.unlink()
|
|
(args.output / 'report.json').write_text(json.dumps(record, indent=2, sort_keys=True) + '\n')
|
|
print(json.dumps({key: record[key] for key in ('success', 'remove', 'purge')}))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|