blob: 44a94f567cd4950a2cd8ea8f81d1e3217131d7a2 [file] [log] [blame]
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright 2018 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Uprevs Termina images"""
import argparse
import json
import os
import shutil
import struct
import subprocess
import sys
import tempfile
import urllib.request
from pathlib import Path
from termina_build_image import repack_rootfs
from termina_util import mount_disk, get_release_version
def get_build_path(board, branch):
query = {
'builder': {
'project': 'chromeos',
'bucket': 'general',
'builder': 'LegacyRelease',
},
'tags': [{
'key': 'cbb_branch',
'value': f'{branch}',
}, {
'key': 'cbb_config',
'value': f'{board}-release',
}],
'status': 'SUCCESS',
}
query_str = json.dumps(query)
result_str = subprocess.run(
['bb', 'ls', '-n', '1', '-json', '-fields', 'output', '-predicate', query_str],
stdout=subprocess.PIPE,
check=True).stdout.decode()
result = json.loads(result_str)
return result['output']['properties']['artifact_link']
def download_image(board, gs_path, output_dir):
target_path = output_dir / '{}_base_image.tar.xz'.format(board)
subprocess.run(
['gsutil', 'cp',
f'{gs_path}/chromiumos_base_image.tar.xz',
target_path],
check=True)
return target_path
def unpack_component(board, image_path, output_dir):
component_dir = output_dir / board
component_dir.mkdir()
result = subprocess.run(['tar', 'xvf', image_path, '-C', str(component_dir)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
return component_dir / 'chromiumos_base_image.bin'
def build_component(board, image_path, output_dir, component_version):
print('Repacking rootfs')
component_dir = output_dir / board
component_dir.mkdir(exist_ok=True)
repack_rootfs(component_dir, image_path)
# Assemble the component disk image.
print('Building component disk image')
# Create image at 150% of source size.
# resize2fs will shrink it to the minimum size later.
du_output = subprocess.check_output(['du', '-bsx',
component_dir]).decode('utf-8')
src_size = int(du_output.split()[0])
img_size = int(src_size * 1.50)
component_disk = component_dir / 'image.ext4'
with component_disk.open('wb+') as component:
component.truncate(img_size)
subprocess.run(['/sbin/mkfs.ext4', '-b', '4096', '-O', '^has_journal', str(component_disk)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
mnt_dir = component_dir / 'mnt'
mnt_dir.mkdir()
with mount_disk(str(component_disk), str(mnt_dir)) as mntpoint:
for component_file in ['about_os_credits.html', 'lsb-release',
'vm_kernel', 'vm_rootfs.img', 'vm_tools.img']:
subprocess.run(['sudo', 'cp', str(component_dir / component_file),
str(mnt_dir / component_file)], check=True)
subprocess.run(['/sbin/e2fsck', '-y', '-f', str(component_disk)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
subprocess.run(['/sbin/resize2fs', '-M', str(component_disk)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
# Create manifest and zip up.
print('Zipping up component and manifest')
release_version = get_release_version(component_dir / 'lsb-release')
if not release_version:
print("No valid version in lsb-release")
sys.exit(1)
manifest_contents = {
'description': 'Chrome OS VM Container',
'name': 'termina',
'version': release_version,
'min_env_version': component_version,
'squash' : 'false',
'image_name' : 'image.ext4',
'fs_type' : 'ext4',
'is_removable' : 'true'
}
manifest_encoded = json.dumps(manifest_contents)
manifest_path = component_dir / 'manifest.json'
with open(str(manifest_path), 'w+') as manifest:
manifest.write(manifest_encoded)
subprocess.run(['zip', '-rj', str(component_dir / 'files.zip'), str(component_disk), str(manifest_path)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
def main():
parser = argparse.ArgumentParser(description='Uprev Termina images')
parser.add_argument('--branch', help='optional branch name, e.g. release-R72-11316.B (default: main)', default='main')
parser.add_argument('--output_dir', help='local dir to save results')
parser.add_argument('component_version', help='version of the component')
args = parser.parse_args()
with tempfile.TemporaryDirectory() as tempdir_path:
tempdir = Path(tempdir_path)
for board in ['tatl', 'tael']:
gs_path = get_build_path(board, args.branch)
download_path = download_image(board, gs_path, tempdir)
image_path = unpack_component(board, download_path, tempdir)
build_component(board, image_path, tempdir, args.component_version)
if args.output_dir:
target_dir = Path(args.output_dir) / board
shutil.copytree(str(tempdir / board), str(target_dir))
sys.exit(0)
if __name__ == '__main__':
main()