52 lines
2.1 KiB
Python
52 lines
2.1 KiB
Python
"""Deterministic provisioning payloads for golden-image builds."""
|
|
import hashlib
|
|
import io
|
|
from pathlib import Path
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import tarfile
|
|
|
|
from .state import ProvisionError
|
|
|
|
|
|
def _bundle_from_source(root):
|
|
root = Path(root).resolve()
|
|
package = root / 'provisioning'
|
|
files = sorted(package.rglob('*.py'))
|
|
seed = package / 'seed.sh'
|
|
guard = package / 'profiles.sh'
|
|
if not seed.is_file() or not (package / '__init__.py').is_file():
|
|
raise ProvisionError('Provisioning package or seed is missing')
|
|
files.append(seed)
|
|
if guard.is_file():
|
|
files.append(guard)
|
|
output = io.BytesIO()
|
|
with tarfile.open(fileobj=output, mode='w') as archive:
|
|
for path in files:
|
|
if (not path.is_file() or '__pycache__' in path.parts
|
|
or any(part.is_symlink() for part in (path, *path.parents))):
|
|
raise ProvisionError('Provisioning bundle contains an unsafe source path')
|
|
data = path.read_bytes()
|
|
info = tarfile.TarInfo(path.relative_to(root).as_posix())
|
|
info.size, info.mode, info.mtime = len(data), (0o755 if path == seed else 0o644), 0
|
|
info.uid = info.gid = 0
|
|
archive.addfile(info, io.BytesIO(data))
|
|
data = output.getvalue()
|
|
return data, hashlib.sha256(data).hexdigest()
|
|
|
|
|
|
def build_bundle(commons):
|
|
root = Path(commons).resolve()
|
|
data, digest = _bundle_from_source(root / 'ci/scripts/init-node')
|
|
revision = 'unversioned-modified'
|
|
if shutil.which('git'):
|
|
result = subprocess.run(['git', '-C', str(root), 'rev-parse', 'HEAD'], capture_output=True, text=True, timeout=30)
|
|
if result.returncode == 0 and re.fullmatch(r'[a-f0-9]{40,64}\n?', result.stdout):
|
|
revision = result.stdout.strip()
|
|
dirty = subprocess.run(['git', '-C', str(root), 'status', '--porcelain', '--', 'ci/scripts/init-node'], capture_output=True, text=True, timeout=30)
|
|
if dirty.returncode or dirty.stdout.strip():
|
|
revision += '-modified'
|
|
return data, digest, revision
|
|
|