mirror of
https://github.com/flatcar/scripts.git
synced 2025-08-09 05:56:58 +02:00
Update test harness to take in optional public and private keys to sign payloads.
In addition some refactoring to make this cleaner and easier. Change-Id: I1607700d065c71aff2b2833b10acbd3ebace68ce BUG=chromium-os:8212 TEST=Ran with Simple ... running now with full Review URL: http://codereview.chromium.org/6482017
This commit is contained in:
parent
1ed7fe6b70
commit
8dad50d97f
@ -15,8 +15,10 @@
|
|||||||
import optparse
|
import optparse
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
import unittest
|
import unittest
|
||||||
@ -53,11 +55,74 @@ class AUTest(object):
|
|||||||
self.crosutils = os.path.join(os.path.dirname(__file__), '..')
|
self.crosutils = os.path.join(os.path.dirname(__file__), '..')
|
||||||
self.crosutilsbin = os.path.join(os.path.dirname(__file__))
|
self.crosutilsbin = os.path.join(os.path.dirname(__file__))
|
||||||
self.download_folder = os.path.join(self.crosutils, 'latest_download')
|
self.download_folder = os.path.join(self.crosutils, 'latest_download')
|
||||||
|
self.vm_image_path = None
|
||||||
if not os.path.exists(self.download_folder):
|
if not os.path.exists(self.download_folder):
|
||||||
os.makedirs(self.download_folder)
|
os.makedirs(self.download_folder)
|
||||||
|
|
||||||
# -------- Helper functions ---------
|
# -------- Helper functions ---------
|
||||||
|
|
||||||
|
def _PrepareRealBase(self, image_path):
|
||||||
|
self.PerformUpdate(image_path)
|
||||||
|
|
||||||
|
def _PrepareVMBase(self, image_path):
|
||||||
|
# VM Constants.
|
||||||
|
FULL_VDISK_SIZE = 6072
|
||||||
|
FULL_STATEFULFS_SIZE = 3074
|
||||||
|
# Needed for VM delta updates. We need to use the qemu image rather
|
||||||
|
# than the base image on a first update. By tracking the first_update
|
||||||
|
# we can set src_image to the qemu form of the base image when
|
||||||
|
# performing generating the delta payload.
|
||||||
|
self._first_update = True
|
||||||
|
self.vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname(
|
||||||
|
image_path)
|
||||||
|
if not os.path.exists(self.vm_image_path):
|
||||||
|
Info('Creating %s' % self.vm_image_path)
|
||||||
|
RunCommand(['%s/image_to_vm.sh' % self.crosutils,
|
||||||
|
'--full',
|
||||||
|
'--from=%s' % ReinterpretPathForChroot(
|
||||||
|
os.path.dirname(image_path)),
|
||||||
|
'--vdisk_size=%s' % FULL_VDISK_SIZE,
|
||||||
|
'--statefulfs_size=%s' % FULL_STATEFULFS_SIZE,
|
||||||
|
'--board=%s' % self.board,
|
||||||
|
'--test_image'], enter_chroot=True)
|
||||||
|
|
||||||
|
Info('Using %s as base' % self.vm_image_path)
|
||||||
|
self.assertTrue(os.path.exists(self.vm_image_path))
|
||||||
|
|
||||||
|
def AppendUpdateFlags(self, cmd, image_path, src_image_path, proxy_port,
|
||||||
|
private_key_path):
|
||||||
|
"""Appends common args to an update cmd defined by an array.
|
||||||
|
|
||||||
|
Modifies cmd in places by appending appropriate items given args.
|
||||||
|
"""
|
||||||
|
if proxy_port: cmd.append('--proxy_port=%s' % proxy_port)
|
||||||
|
|
||||||
|
# Get pregenerated update if we have one.
|
||||||
|
update_id = _GenerateUpdateId(target=image_path, src=src_image_path,
|
||||||
|
key=private_key_path)
|
||||||
|
cache_path = dev_server_cache[update_id]
|
||||||
|
if cache_path:
|
||||||
|
update_url = DevServerWrapper.GetDevServerURL(proxy_port, cache_path)
|
||||||
|
cmd.append('--update_url=%s' % update_url)
|
||||||
|
else:
|
||||||
|
cmd.append('--image=%s' % image_path)
|
||||||
|
if src_image_path: cmd.append('--src_image=%s' % src_image_path)
|
||||||
|
|
||||||
|
def RunUpdateCmd(self, cmd):
|
||||||
|
"""Runs the given update cmd given verbose options.
|
||||||
|
|
||||||
|
Raises an UpdateException if the update fails.
|
||||||
|
"""
|
||||||
|
if self.verbose:
|
||||||
|
try:
|
||||||
|
RunCommand(cmd)
|
||||||
|
except Exception, e:
|
||||||
|
raise UpdateException(1, e.message)
|
||||||
|
else:
|
||||||
|
(code, stdout, stderr) = RunCommandCaptureOutput(cmd)
|
||||||
|
if code != 0:
|
||||||
|
raise UpdateException(code, stdout)
|
||||||
|
|
||||||
def GetStatefulChangeFlag(self, stateful_change):
|
def GetStatefulChangeFlag(self, stateful_change):
|
||||||
"""Returns the flag to pass to image_to_vm for the stateful change."""
|
"""Returns the flag to pass to image_to_vm for the stateful change."""
|
||||||
stateful_change_flag = ''
|
stateful_change_flag = ''
|
||||||
@ -101,7 +166,7 @@ class AUTest(object):
|
|||||||
return percent_passed
|
return percent_passed
|
||||||
|
|
||||||
def PerformUpdate(self, image_path, src_image_path='', stateful_change='old',
|
def PerformUpdate(self, image_path, src_image_path='', stateful_change='old',
|
||||||
proxy_port=None):
|
proxy_port=None, private_key_path=None):
|
||||||
"""Performs an update using _UpdateImage and reports any error.
|
"""Performs an update using _UpdateImage and reports any error.
|
||||||
|
|
||||||
Subclasses should not override this method but override _UpdateImage
|
Subclasses should not override this method but override _UpdateImage
|
||||||
@ -121,10 +186,14 @@ class AUTest(object):
|
|||||||
Raises an UpdateException if _UpdateImage returns an error.
|
Raises an UpdateException if _UpdateImage returns an error.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not self.use_delta_updates:
|
if not self.use_delta_updates: src_image_path = ''
|
||||||
src_image_path = ''
|
if private_key_path:
|
||||||
|
key_to_use = private_key_path
|
||||||
|
else:
|
||||||
|
key_to_use = self.private_key
|
||||||
|
|
||||||
self._UpdateImage(image_path, src_image_path, stateful_change, proxy_port)
|
self._UpdateImage(image_path, src_image_path, stateful_change, proxy_port,
|
||||||
|
key_to_use)
|
||||||
except UpdateException as err:
|
except UpdateException as err:
|
||||||
# If the update fails, print it out
|
# If the update fails, print it out
|
||||||
Warning(err.stdout)
|
Warning(err.stdout)
|
||||||
@ -178,6 +247,9 @@ class AUTest(object):
|
|||||||
cls.base_image_path = options.base_image
|
cls.base_image_path = options.base_image
|
||||||
cls.target_image_path = options.target_image
|
cls.target_image_path = options.target_image
|
||||||
cls.use_delta_updates = options.delta
|
cls.use_delta_updates = options.delta
|
||||||
|
cls.board = options.board
|
||||||
|
cls.private_key = options.private_key
|
||||||
|
cls.clean = options.clean
|
||||||
if options.quick_test:
|
if options.quick_test:
|
||||||
cls.verify_suite = 'build_RootFilesystemSize'
|
cls.verify_suite = 'build_RootFilesystemSize'
|
||||||
else:
|
else:
|
||||||
@ -199,7 +271,7 @@ class AUTest(object):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def _UpdateImage(self, image_path, src_image_path='', stateful_change='old',
|
def _UpdateImage(self, image_path, src_image_path='', stateful_change='old',
|
||||||
proxy_port=None):
|
proxy_port=None, private_key_path=None):
|
||||||
"""Implementation of an actual update.
|
"""Implementation of an actual update.
|
||||||
|
|
||||||
See PerformUpdate for description of args. Subclasses must override this
|
See PerformUpdate for description of args. Subclasses must override this
|
||||||
@ -410,32 +482,20 @@ class RealAUTest(unittest.TestCase, AUTest):
|
|||||||
|
|
||||||
def PrepareBase(self, image_path):
|
def PrepareBase(self, image_path):
|
||||||
"""Auto-update to base image to prepare for test."""
|
"""Auto-update to base image to prepare for test."""
|
||||||
self.PerformUpdate(image_path)
|
_PrepareRealBase(image_path)
|
||||||
|
|
||||||
def _UpdateImage(self, image_path, src_image_path='', stateful_change='old',
|
def _UpdateImage(self, image_path, src_image_path='', stateful_change='old',
|
||||||
proxy_port=None):
|
proxy_port=None, private_key_path=None):
|
||||||
"""Updates a remote image using image_to_live.sh."""
|
"""Updates a remote image using image_to_live.sh."""
|
||||||
stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
|
stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
|
||||||
cmd = ['%s/image_to_live.sh' % self.crosutils,
|
cmd = ['%s/image_to_live.sh' % self.crosutils,
|
||||||
'--image=%s' % image_path,
|
|
||||||
'--remote=%s' % self.remote,
|
'--remote=%s' % self.remote,
|
||||||
stateful_change_flag,
|
stateful_change_flag,
|
||||||
'--verify',
|
'--verify',
|
||||||
'--src_image=%s' % src_image_path
|
|
||||||
]
|
]
|
||||||
|
self.AppendUpdateFlags(cmd, image_path, src_image_path, proxy_port,
|
||||||
if proxy_port:
|
private_key_path)
|
||||||
cmd.append('--proxy_port=%s' % proxy_port)
|
self.RunUpdateCmd(cmd)
|
||||||
|
|
||||||
if self.verbose:
|
|
||||||
try:
|
|
||||||
RunCommand(cmd)
|
|
||||||
except Exception, e:
|
|
||||||
raise UpdateException(1, e.message)
|
|
||||||
else:
|
|
||||||
(code, stdout, stderr) = RunCommandCaptureOutput(cmd)
|
|
||||||
if code != 0:
|
|
||||||
raise UpdateException(code, stdout)
|
|
||||||
|
|
||||||
def _UpdateUsingPayload(self, update_path, stateful_change='old',
|
def _UpdateUsingPayload(self, update_path, stateful_change='old',
|
||||||
proxy_port=None):
|
proxy_port=None):
|
||||||
@ -447,19 +507,8 @@ class RealAUTest(unittest.TestCase, AUTest):
|
|||||||
stateful_change_flag,
|
stateful_change_flag,
|
||||||
'--verify',
|
'--verify',
|
||||||
]
|
]
|
||||||
|
if proxy_port: cmd.append('--proxy_port=%s' % proxy_port)
|
||||||
if proxy_port:
|
self.RunUpdateCmd(cmd)
|
||||||
cmd.append('--proxy_port=%s' % proxy_port)
|
|
||||||
|
|
||||||
if self.verbose:
|
|
||||||
try:
|
|
||||||
RunCommand(cmd)
|
|
||||||
except Exception, e:
|
|
||||||
raise UpdateException(1, e.message)
|
|
||||||
else:
|
|
||||||
(code, stdout, stderr) = RunCommandCaptureOutput(cmd)
|
|
||||||
if code != 0:
|
|
||||||
raise UpdateException(code, stdout)
|
|
||||||
|
|
||||||
def VerifyImage(self, percent_required_to_pass):
|
def VerifyImage(self, percent_required_to_pass):
|
||||||
"""Verifies an image using run_remote_tests.sh with verification suite."""
|
"""Verifies an image using run_remote_tests.sh with verification suite."""
|
||||||
@ -474,10 +523,6 @@ class RealAUTest(unittest.TestCase, AUTest):
|
|||||||
class VirtualAUTest(unittest.TestCase, AUTest):
|
class VirtualAUTest(unittest.TestCase, AUTest):
|
||||||
"""Test harness for updating virtual machines."""
|
"""Test harness for updating virtual machines."""
|
||||||
|
|
||||||
# VM Constants.
|
|
||||||
_FULL_VDISK_SIZE = 6072
|
|
||||||
_FULL_STATEFULFS_SIZE = 3074
|
|
||||||
|
|
||||||
# Class variables used to acquire individual VM variables per test.
|
# Class variables used to acquire individual VM variables per test.
|
||||||
_vm_lock = threading.Lock()
|
_vm_lock = threading.Lock()
|
||||||
_next_port = 9222
|
_next_port = 9222
|
||||||
@ -501,7 +546,6 @@ class VirtualAUTest(unittest.TestCase, AUTest):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""Unit test overriden method. Is called before every test."""
|
"""Unit test overriden method. Is called before every test."""
|
||||||
AUTest.setUp(self)
|
AUTest.setUp(self)
|
||||||
self.vm_image_path = None
|
|
||||||
self._AcquireUniquePortAndPidFile()
|
self._AcquireUniquePortAndPidFile()
|
||||||
self._KillExistingVM(self._kvm_pid_file)
|
self._KillExistingVM(self._kvm_pid_file)
|
||||||
|
|
||||||
@ -512,52 +556,24 @@ class VirtualAUTest(unittest.TestCase, AUTest):
|
|||||||
def ProcessOptions(cls, parser, options):
|
def ProcessOptions(cls, parser, options):
|
||||||
"""Processes vm-specific options."""
|
"""Processes vm-specific options."""
|
||||||
AUTest.ProcessOptions(parser, options)
|
AUTest.ProcessOptions(parser, options)
|
||||||
cls.board = options.board
|
|
||||||
|
|
||||||
# Communicate flags to tests.
|
# Communicate flags to tests.
|
||||||
cls.graphics_flag = ''
|
cls.graphics_flag = ''
|
||||||
if options.no_graphics: cls.graphics_flag = '--no_graphics'
|
if options.no_graphics: cls.graphics_flag = '--no_graphics'
|
||||||
|
if not cls.board: parser.error('Need board to convert base image to vm.')
|
||||||
if not cls.board:
|
|
||||||
parser.error('Need board to convert base image to vm.')
|
|
||||||
|
|
||||||
def PrepareBase(self, image_path):
|
def PrepareBase(self, image_path):
|
||||||
"""Creates an update-able VM based on base image."""
|
"""Creates an update-able VM based on base image."""
|
||||||
# Needed for VM delta updates. We need to use the qemu image rather
|
self._PrepareVMBase(image_path)
|
||||||
# than the base image on a first update. By tracking the first_update
|
|
||||||
# we can set src_image to the qemu form of the base image when
|
|
||||||
# performing generating the delta payload.
|
|
||||||
self._first_update = True
|
|
||||||
self.vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname(
|
|
||||||
image_path)
|
|
||||||
if not os.path.exists(self.vm_image_path):
|
|
||||||
Info('Creating %s' % vm_image_path)
|
|
||||||
RunCommand(['%s/image_to_vm.sh' % self.crosutils,
|
|
||||||
'--full',
|
|
||||||
'--from=%s' % ReinterpretPathForChroot(
|
|
||||||
os.path.dirname(image_path)),
|
|
||||||
'--vdisk_size=%s' % self._FULL_VDISK_SIZE,
|
|
||||||
'--statefulfs_size=%s' % self._FULL_STATEFULFS_SIZE,
|
|
||||||
'--board=%s' % self.board,
|
|
||||||
'--test_image'], enter_chroot=True)
|
|
||||||
|
|
||||||
Info('Using %s as base' % self.vm_image_path)
|
|
||||||
self.assertTrue(os.path.exists(self.vm_image_path))
|
|
||||||
|
|
||||||
def _UpdateImage(self, image_path, src_image_path='', stateful_change='old',
|
def _UpdateImage(self, image_path, src_image_path='', stateful_change='old',
|
||||||
proxy_port=''):
|
proxy_port='', private_key_path=None):
|
||||||
"""Updates VM image with image_path."""
|
"""Updates VM image with image_path."""
|
||||||
stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
|
stateful_change_flag = self.GetStatefulChangeFlag(stateful_change)
|
||||||
if src_image_path and self._first_update:
|
if src_image_path and self._first_update:
|
||||||
src_image_path = self.vm_image_path
|
src_image_path = self.vm_image_path
|
||||||
self._first_update = False
|
self._first_update = False
|
||||||
|
|
||||||
# Check image payload cache first.
|
|
||||||
update_id = _GenerateUpdateId(target=image_path, src=src_image_path)
|
|
||||||
cache_path = dev_server_cache[update_id]
|
|
||||||
if cache_path:
|
|
||||||
Info('Using cache %s' % cache_path)
|
|
||||||
update_url = DevServerWrapper.GetDevServerURL(proxy_port, cache_path)
|
|
||||||
cmd = ['%s/cros_run_vm_update' % self.crosutilsbin,
|
cmd = ['%s/cros_run_vm_update' % self.crosutilsbin,
|
||||||
'--vm_image_path=%s' % self.vm_image_path,
|
'--vm_image_path=%s' % self.vm_image_path,
|
||||||
'--snapshot',
|
'--snapshot',
|
||||||
@ -566,31 +582,11 @@ class VirtualAUTest(unittest.TestCase, AUTest):
|
|||||||
'--kvm_pid=%s' % self._kvm_pid_file,
|
'--kvm_pid=%s' % self._kvm_pid_file,
|
||||||
'--ssh_port=%s' % self._ssh_port,
|
'--ssh_port=%s' % self._ssh_port,
|
||||||
stateful_change_flag,
|
stateful_change_flag,
|
||||||
'--update_url=%s' % update_url,
|
|
||||||
]
|
|
||||||
else:
|
|
||||||
cmd = ['%s/cros_run_vm_update' % self.crosutilsbin,
|
|
||||||
'--update_image_path=%s' % image_path,
|
|
||||||
'--vm_image_path=%s' % self.vm_image_path,
|
|
||||||
'--snapshot',
|
|
||||||
self.graphics_flag,
|
|
||||||
'--persist',
|
|
||||||
'--kvm_pid=%s' % self._kvm_pid_file,
|
|
||||||
'--ssh_port=%s' % self._ssh_port,
|
|
||||||
stateful_change_flag,
|
|
||||||
'--src_image=%s' % src_image_path,
|
|
||||||
'--proxy_port=%s' % proxy_port
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if self.verbose:
|
self.AppendUpdateFlags(cmd, image_path, src_image_path, proxy_port,
|
||||||
try:
|
private_key_path)
|
||||||
RunCommand(cmd)
|
self.RunUpdateCmd(cmd)
|
||||||
except Exception, e:
|
|
||||||
raise UpdateException(1, e.message)
|
|
||||||
else:
|
|
||||||
(code, stdout, stderr) = RunCommandCaptureOutput(cmd)
|
|
||||||
if code != 0:
|
|
||||||
raise UpdateException(code, stdout)
|
|
||||||
|
|
||||||
def _UpdateUsingPayload(self, update_path, stateful_change='old',
|
def _UpdateUsingPayload(self, update_path, stateful_change='old',
|
||||||
proxy_port=None):
|
proxy_port=None):
|
||||||
@ -606,19 +602,8 @@ class VirtualAUTest(unittest.TestCase, AUTest):
|
|||||||
'--ssh_port=%s' % self._ssh_port,
|
'--ssh_port=%s' % self._ssh_port,
|
||||||
stateful_change_flag,
|
stateful_change_flag,
|
||||||
]
|
]
|
||||||
|
if proxy_port: cmd.append('--proxy_port=%s' % proxy_port)
|
||||||
if proxy_port:
|
self.RunUpdateCmd(cmd)
|
||||||
cmd.append('--proxy_port=%s' % proxy_port)
|
|
||||||
|
|
||||||
if self.verbose:
|
|
||||||
try:
|
|
||||||
RunCommand(cmd)
|
|
||||||
except Exception, e:
|
|
||||||
raise UpdateException(1, e.message)
|
|
||||||
else:
|
|
||||||
(code, stdout, stderr) = RunCommandCaptureOutput(cmd)
|
|
||||||
if code != 0:
|
|
||||||
raise UpdateException(code, stdout)
|
|
||||||
|
|
||||||
def VerifyImage(self, percent_required_to_pass):
|
def VerifyImage(self, percent_required_to_pass):
|
||||||
"""Runs vm smoke suite to verify image."""
|
"""Runs vm smoke suite to verify image."""
|
||||||
@ -642,8 +627,12 @@ class VirtualAUTest(unittest.TestCase, AUTest):
|
|||||||
return self.AssertEnoughTestsPassed(self, output, percent_required_to_pass)
|
return self.AssertEnoughTestsPassed(self, output, percent_required_to_pass)
|
||||||
|
|
||||||
|
|
||||||
class GenerateVirtualAUDeltasTest(VirtualAUTest):
|
class PregenerateAUDeltas(unittest.TestCase, AUTest):
|
||||||
"""Class the overrides VirtualAUTest and stores deltas we will generate."""
|
"""Magical class that emulates an AUTest to store deltas we will generate.
|
||||||
|
|
||||||
|
This class emulates an AUTest such that when it runs as a TestCase it runs
|
||||||
|
through the exact up
|
||||||
|
"""
|
||||||
delta_list = {}
|
delta_list = {}
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -652,16 +641,29 @@ class GenerateVirtualAUDeltasTest(VirtualAUTest):
|
|||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def ProcessOptions(cls, parser, options):
|
||||||
|
AUTest.ProcessOptions(parser, options)
|
||||||
|
cls.au_type = options.type
|
||||||
|
|
||||||
|
def PrepareBase(self, image_path):
|
||||||
|
if self.au_type == 'vm':
|
||||||
|
self._PrepareVMBase(image_path)
|
||||||
|
else:
|
||||||
|
self._PrepareRealBase(image_path)
|
||||||
|
|
||||||
def _UpdateImage(self, image_path, src_image_path='', stateful_change='old',
|
def _UpdateImage(self, image_path, src_image_path='', stateful_change='old',
|
||||||
proxy_port=None):
|
proxy_port=None, private_key_path=None):
|
||||||
if src_image_path and self._first_update:
|
if self.au_type == 'vm' and src_image_path and self._first_update:
|
||||||
src_image_path = self.vm_image_path
|
src_image_path = self.vm_image_path
|
||||||
self._first_update = False
|
self._first_update = False
|
||||||
|
|
||||||
|
# Generate a value that combines delta with private key path.
|
||||||
|
val = '%s+%s' % (src_image_path, private_key_path)
|
||||||
if not self.delta_list.has_key(image_path):
|
if not self.delta_list.has_key(image_path):
|
||||||
self.delta_list[image_path] = set([src_image_path])
|
self.delta_list[image_path] = set([val])
|
||||||
else:
|
else:
|
||||||
self.delta_list[image_path].add(src_image_path)
|
self.delta_list[image_path].add(val)
|
||||||
|
|
||||||
def AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg):
|
def AttemptUpdateWithPayloadExpectedFailure(self, payload, expected_msg):
|
||||||
pass
|
pass
|
||||||
@ -756,15 +758,16 @@ class DevServerWrapper(threading.Thread):
|
|||||||
return url
|
return url
|
||||||
|
|
||||||
|
|
||||||
def _GenerateUpdateId(target, src):
|
def _GenerateUpdateId(target, src, key):
|
||||||
"""Returns a simple representation id of target and src paths."""
|
"""Returns a simple representation id of target and src paths."""
|
||||||
if src:
|
update_id = target
|
||||||
return '%s->%s' % (target, src)
|
if src: update_id = '->'.join([update_id, src])
|
||||||
else:
|
if key: update_id = '+'.join([update_id, key])
|
||||||
return target
|
return update_id
|
||||||
|
|
||||||
|
|
||||||
def _RunParallelJobs(number_of_sumultaneous_jobs, jobs, jobs_args, print_status):
|
def _RunParallelJobs(number_of_sumultaneous_jobs, jobs, jobs_args,
|
||||||
|
print_status):
|
||||||
|
|
||||||
"""Runs set number of specified jobs in parallel.
|
"""Runs set number of specified jobs in parallel.
|
||||||
|
|
||||||
@ -834,40 +837,47 @@ def _PregenerateUpdates(parser, options):
|
|||||||
Raises:
|
Raises:
|
||||||
UpdateException if we fail to generate an update.
|
UpdateException if we fail to generate an update.
|
||||||
"""
|
"""
|
||||||
def _GenerateVMUpdate(target, src):
|
def _GenerateVMUpdate(target, src, private_key_path):
|
||||||
"""Generates an update using the devserver."""
|
"""Generates an update using the devserver."""
|
||||||
target = ReinterpretPathForChroot(target)
|
command = ['./enter_chroot.sh',
|
||||||
if src:
|
|
||||||
src = ReinterpretPathForChroot(src)
|
|
||||||
|
|
||||||
return RunCommandCaptureOutput(['./enter_chroot.sh',
|
|
||||||
'--nogit_config',
|
'--nogit_config',
|
||||||
'--',
|
'--',
|
||||||
'sudo',
|
'sudo',
|
||||||
'./start_devserver',
|
'./start_devserver',
|
||||||
'--pregenerate_update',
|
'--pregenerate_update',
|
||||||
'--exit',
|
'--exit',
|
||||||
'--image=%s' % target,
|
]
|
||||||
'--src_image=%s' % src,
|
# Add actual args to command.
|
||||||
'--for_vm',
|
command.append('--image=%s' % ReinterpretPathForChroot(target))
|
||||||
], combine_stdout_stderr=True,
|
if src: command.append('--src_image=%s' % ReinterpretPathForChroot(src))
|
||||||
print_cmd=False)
|
if options.type == 'vm': command.append('--for_vm')
|
||||||
|
if private_key_path:
|
||||||
|
command.append('--private_key=%s' %
|
||||||
|
ReinterpretPathForChroot(private_key_path))
|
||||||
|
|
||||||
|
return RunCommandCaptureOutput(command, combine_stdout_stderr=True,
|
||||||
|
print_cmd=True)
|
||||||
|
|
||||||
# Get the list of deltas by mocking out update method in test class.
|
# Get the list of deltas by mocking out update method in test class.
|
||||||
test_suite = _PrepareTestSuite(parser, options, GenerateVirtualAUDeltasTest)
|
test_suite = _PrepareTestSuite(parser, options, PregenerateAUDeltas)
|
||||||
test_result = unittest.TextTestRunner(verbosity=0).run(test_suite)
|
test_result = unittest.TextTestRunner(verbosity=0).run(test_suite)
|
||||||
|
if not test_result.wasSuccessful():
|
||||||
|
raise UpdateException(1, 'Error finding updates to generate.')
|
||||||
|
|
||||||
Info('The following delta updates are required.')
|
Info('The following delta updates are required.')
|
||||||
update_ids = []
|
update_ids = []
|
||||||
jobs = []
|
jobs = []
|
||||||
args = []
|
args = []
|
||||||
for target, srcs in GenerateVirtualAUDeltasTest.delta_list.items():
|
for target, srcs in PregenerateAUDeltas.delta_list.items():
|
||||||
for src in srcs:
|
for src_key in srcs:
|
||||||
update_id = _GenerateUpdateId(target=target, src=src)
|
(src, key) = src_key.split('+')
|
||||||
|
# TODO(sosa): Add private key as part of caching name once devserver can
|
||||||
|
# handle it its own cache.
|
||||||
|
update_id = _GenerateUpdateId(target=target, src=src, key=key)
|
||||||
print >> sys.stderr, 'AU: %s' % update_id
|
print >> sys.stderr, 'AU: %s' % update_id
|
||||||
update_ids.append(update_id)
|
update_ids.append(update_id)
|
||||||
jobs.append(_GenerateVMUpdate)
|
jobs.append(_GenerateVMUpdate)
|
||||||
args.append((target, src))
|
args.append((target, src, key))
|
||||||
|
|
||||||
raw_results = _RunParallelJobs(options.jobs, jobs, args, print_status=True)
|
raw_results = _RunParallelJobs(options.jobs, jobs, args, print_status=True)
|
||||||
results = []
|
results = []
|
||||||
@ -919,12 +929,74 @@ def _RunTestsInParallel(parser, options, test_class):
|
|||||||
Die('Test harness was not successful')
|
Die('Test harness was not successful')
|
||||||
|
|
||||||
|
|
||||||
|
def InsertPublicKeyIntoImage(image_path, key_path):
|
||||||
|
"""Inserts public key into image @ static update_engine location."""
|
||||||
|
from_dir = os.path.dirname(image_path)
|
||||||
|
image = os.path.basename(image_path)
|
||||||
|
crosutils_dir = os.path.abspath(__file__).rsplit('/', 2)[0]
|
||||||
|
target_key_path = 'usr/share/update_engine/update-payload-key.pub.pem'
|
||||||
|
|
||||||
|
# Temporary directories for this function.
|
||||||
|
rootfs_dir = tempfile.mkdtemp(suffix='rootfs', prefix='tmp')
|
||||||
|
stateful_dir = tempfile.mkdtemp(suffix='stateful', prefix='tmp')
|
||||||
|
|
||||||
|
Info('Copying %s into %s' % (key_path, image_path))
|
||||||
|
try:
|
||||||
|
RunCommand(['./mount_gpt_image.sh',
|
||||||
|
'--from=%s' % from_dir,
|
||||||
|
'--image=%s' % image,
|
||||||
|
'--rootfs_mountpt=%s' % rootfs_dir,
|
||||||
|
'--stateful_mountpt=%s' % stateful_dir,
|
||||||
|
], print_cmd=False, redirect_stdout=True,
|
||||||
|
redirect_stderr=True, cwd=crosutils_dir)
|
||||||
|
path = os.path.join(rootfs_dir, target_key_path)
|
||||||
|
dir_path = os.path.dirname(path)
|
||||||
|
RunCommand(['sudo', 'mkdir', '--parents', dir_path], print_cmd=False)
|
||||||
|
RunCommand(['sudo', 'cp', '--force', '-p', key_path, path],
|
||||||
|
print_cmd=False)
|
||||||
|
finally:
|
||||||
|
# Unmount best effort regardless.
|
||||||
|
RunCommand(['./mount_gpt_image.sh',
|
||||||
|
'--unmount',
|
||||||
|
'--rootfs_mountpt=%s' % rootfs_dir,
|
||||||
|
'--stateful_mountpt=%s' % stateful_dir,
|
||||||
|
], print_cmd=False, redirect_stdout=True, redirect_stderr=True,
|
||||||
|
cwd=crosutils_dir)
|
||||||
|
# Clean up our directories.
|
||||||
|
os.rmdir(rootfs_dir)
|
||||||
|
os.rmdir(stateful_dir)
|
||||||
|
|
||||||
|
RunCommand(['bin/cros_make_image_bootable', from_dir, image, ],
|
||||||
|
print_cmd=False, redirect_stdout=True, redirect_stderr=True,
|
||||||
|
enter_chroot=True, cwd=crosutils_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def CleanPreviousWork(options):
|
||||||
|
"""Cleans up previous work from the devserver cache and local image cache."""
|
||||||
|
Info('Cleaning up previous work.')
|
||||||
|
# Wipe devserver cache.
|
||||||
|
RunCommandCaptureOutput(
|
||||||
|
['sudo', './start_devserver', '--clear_cache', '--exit', ],
|
||||||
|
enter_chroot=True, print_cmd=False, combine_stdout_stderr=True)
|
||||||
|
|
||||||
|
# Clean previous vm images if they exist.
|
||||||
|
if options.type == 'vm':
|
||||||
|
target_vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname(
|
||||||
|
options.target_image)
|
||||||
|
base_vm_image_path = '%s/chromiumos_qemu_image.bin' % os.path.dirname(
|
||||||
|
options.base_image)
|
||||||
|
if os.path.exists(target_vm_image_path): os.remove(target_vm_image_path)
|
||||||
|
if os.path.exists(base_vm_image_path): os.remove(base_vm_image_path)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = optparse.OptionParser()
|
parser = optparse.OptionParser()
|
||||||
parser.add_option('-b', '--base_image',
|
parser.add_option('-b', '--base_image',
|
||||||
help='path to the base image.')
|
help='path to the base image.')
|
||||||
parser.add_option('-r', '--board',
|
parser.add_option('-r', '--board',
|
||||||
help='board for the images.')
|
help='board for the images.')
|
||||||
|
parser.add_option('--clean', default=False, dest='clean', action='store_true',
|
||||||
|
help='Clean all previous state')
|
||||||
parser.add_option('--no_delta', action='store_false', default=True,
|
parser.add_option('--no_delta', action='store_false', default=True,
|
||||||
dest='delta',
|
dest='delta',
|
||||||
help='Disable using delta updates.')
|
help='Disable using delta updates.')
|
||||||
@ -932,6 +1004,10 @@ def main():
|
|||||||
help='Disable graphics for the vm test.')
|
help='Disable graphics for the vm test.')
|
||||||
parser.add_option('-j', '--jobs', default=8, type=int,
|
parser.add_option('-j', '--jobs', default=8, type=int,
|
||||||
help='Number of simultaneous jobs')
|
help='Number of simultaneous jobs')
|
||||||
|
parser.add_option('--public_key', default=None,
|
||||||
|
help='Public key to use on images and updates.')
|
||||||
|
parser.add_option('--private_key', default=None,
|
||||||
|
help='Private key to use on images and updates.')
|
||||||
parser.add_option('-q', '--quick_test', default=False, action='store_true',
|
parser.add_option('-q', '--quick_test', default=False, action='store_true',
|
||||||
help='Use a basic test to verify image.')
|
help='Use a basic test to verify image.')
|
||||||
parser.add_option('-m', '--remote',
|
parser.add_option('-m', '--remote',
|
||||||
@ -948,31 +1024,52 @@ def main():
|
|||||||
'possible.')
|
'possible.')
|
||||||
(options, leftover_args) = parser.parse_args()
|
(options, leftover_args) = parser.parse_args()
|
||||||
|
|
||||||
if leftover_args:
|
if leftover_args: parser.error('Found unsupported flags: %s' % leftover_args)
|
||||||
parser.error('Found extra options we do not support: %s' % leftover_args)
|
|
||||||
|
assert options.target_image and os.path.exists(options.target_image), \
|
||||||
|
'Target image path does not exist'
|
||||||
|
if not options.base_image:
|
||||||
|
Info('Base image not specified. Using target image as base image.')
|
||||||
|
options.base_image = options.target_image
|
||||||
|
|
||||||
|
# Sanity checks on keys and insert them onto the image. The caches must be
|
||||||
|
# cleaned so we know that the vm images and payloads match the possibly new
|
||||||
|
# key.
|
||||||
|
if options.private_key or options.public_key:
|
||||||
|
error_msg = ('Could not find %s key. Both private and public keys must be '
|
||||||
|
'specified if either is specified.')
|
||||||
|
assert options.private_key and os.path.exists(options.private_key), \
|
||||||
|
error_msg % 'private'
|
||||||
|
assert options.public_key and os.path.exists(options.public_key), \
|
||||||
|
error_msg % 'public'
|
||||||
|
InsertPublicKeyIntoImage(options.target_image, options.public_key)
|
||||||
|
InsertPublicKeyIntoImage(options.base_image, options.public_key)
|
||||||
|
options.clean = True
|
||||||
|
|
||||||
|
# Clean up previous work if requested.
|
||||||
|
if options.clean: CleanPreviousWork(options)
|
||||||
|
|
||||||
# Figure out the test_class.
|
# Figure out the test_class.
|
||||||
if options.type == 'vm': test_class = VirtualAUTest
|
if options.type == 'vm': test_class = VirtualAUTest
|
||||||
elif options.type == 'real': test_class = RealAUTest
|
elif options.type == 'real': test_class = RealAUTest
|
||||||
else: parser.error('Could not parse harness type %s.' % options.type)
|
else: parser.error('Could not parse harness type %s.' % options.type)
|
||||||
|
|
||||||
# TODO(sosa): Caching doesn't really make sense on non-vm images (yet).
|
# Generate cache of updates to use during test harness.
|
||||||
global dev_server_cache
|
global dev_server_cache
|
||||||
if options.type == 'vm' and options.jobs > 1:
|
|
||||||
dev_server_cache = _PregenerateUpdates(parser, options)
|
dev_server_cache = _PregenerateUpdates(parser, options)
|
||||||
my_server = DevServerWrapper()
|
my_server = DevServerWrapper()
|
||||||
my_server.start()
|
my_server.start()
|
||||||
try:
|
try:
|
||||||
|
if options.type == 'vm':
|
||||||
_RunTestsInParallel(parser, options, test_class)
|
_RunTestsInParallel(parser, options, test_class)
|
||||||
finally:
|
|
||||||
my_server.Stop()
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
dev_server_cache = None
|
# TODO(sosa) - Take in a machine pool for a real test.
|
||||||
|
# Can't run in parallel with only one remote device.
|
||||||
test_suite = _PrepareTestSuite(parser, options, test_class)
|
test_suite = _PrepareTestSuite(parser, options, test_class)
|
||||||
test_result = unittest.TextTestRunner(verbosity=2).run(test_suite)
|
test_result = unittest.TextTestRunner(verbosity=2).run(test_suite)
|
||||||
if not test_result.wasSuccessful():
|
if not test_result.wasSuccessful(): Die('Test harness failed.')
|
||||||
Die('Test harness was not successful.')
|
finally:
|
||||||
|
my_server.Stop()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -35,7 +35,7 @@ DEFINE_string proxy_port "" \
|
|||||||
DEFINE_string src_image "" \
|
DEFINE_string src_image "" \
|
||||||
"Create a delta update by passing in the image on the remote machine."
|
"Create a delta update by passing in the image on the remote machine."
|
||||||
DEFINE_string stateful_update_flag "" "Flags to pass to stateful update." s
|
DEFINE_string stateful_update_flag "" "Flags to pass to stateful update." s
|
||||||
DEFINE_string update_image_path "" "Path of the image to update to." u
|
DEFINE_string image "" "Path of the image to update to." u
|
||||||
DEFINE_string update_url "" "Full url of an update image."
|
DEFINE_string update_url "" "Full url of an update image."
|
||||||
DEFINE_string vm_image_path "" "Path of the VM image to update from." v
|
DEFINE_string vm_image_path "" "Path of the VM image to update from." v
|
||||||
|
|
||||||
@ -52,8 +52,8 @@ trap stop_kvm EXIT
|
|||||||
start_kvm "${FLAGS_vm_image_path}"
|
start_kvm "${FLAGS_vm_image_path}"
|
||||||
retry_until_ssh
|
retry_until_ssh
|
||||||
|
|
||||||
if [ -n "${FLAGS_update_image_path}" ]; then
|
if [ -n "${FLAGS_image}" ]; then
|
||||||
IMAGE_ARGS="--image=$(readlink -f ${FLAGS_update_image_path})"
|
IMAGE_ARGS="--image=$(readlink -f ${FLAGS_image})"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "${FLAGS_payload}" ]; then
|
if [ -n "${FLAGS_payload}" ]; then
|
||||||
|
41
bin/ctest.py
41
bin/ctest.py
@ -225,17 +225,8 @@ def GrabZipAndExtractImage(zip_url, download_folder, image_name) :
|
|||||||
fh.close()
|
fh.close()
|
||||||
|
|
||||||
|
|
||||||
def WipeDevServerCache():
|
|
||||||
"""Wipes the cache of the dev server."""
|
|
||||||
RunCommand(['sudo',
|
|
||||||
'./start_devserver',
|
|
||||||
'--clear_cache',
|
|
||||||
'--exit',
|
|
||||||
], enter_chroot=True)
|
|
||||||
|
|
||||||
|
|
||||||
def RunAUTestHarness(board, channel, latest_url_base, zip_server_base,
|
def RunAUTestHarness(board, channel, latest_url_base, zip_server_base,
|
||||||
no_graphics, type, remote):
|
no_graphics, type, remote, clean):
|
||||||
"""Runs the auto update test harness.
|
"""Runs the auto update test harness.
|
||||||
|
|
||||||
The auto update test harness encapsulates testing the auto-update mechanism
|
The auto update test harness encapsulates testing the auto-update mechanism
|
||||||
@ -251,6 +242,7 @@ def RunAUTestHarness(board, channel, latest_url_base, zip_server_base,
|
|||||||
no_graphics: boolean - If True, disable graphics during vm test.
|
no_graphics: boolean - If True, disable graphics during vm test.
|
||||||
type: which test harness to run. Possible values: real, vm.
|
type: which test harness to run. Possible values: real, vm.
|
||||||
remote: ip address for real test harness run.
|
remote: ip address for real test harness run.
|
||||||
|
clean: Clean the state of test harness before running.
|
||||||
"""
|
"""
|
||||||
crosutils_root = os.path.join(os.path.dirname(__file__), '..')
|
crosutils_root = os.path.join(os.path.dirname(__file__), '..')
|
||||||
download_folder = os.path.abspath('latest_download')
|
download_folder = os.path.abspath('latest_download')
|
||||||
@ -262,6 +254,9 @@ def RunAUTestHarness(board, channel, latest_url_base, zip_server_base,
|
|||||||
cwd=crosutils_root, redirect_stdout=True,
|
cwd=crosutils_root, redirect_stdout=True,
|
||||||
print_cmd=True).strip()
|
print_cmd=True).strip()
|
||||||
|
|
||||||
|
update_engine_path = os.path.join(crosutils_root, '..', 'platform',
|
||||||
|
'update_engine')
|
||||||
|
|
||||||
cmd = ['bin/cros_au_test_harness',
|
cmd = ['bin/cros_au_test_harness',
|
||||||
'--base_image=%s' % os.path.join(download_folder,
|
'--base_image=%s' % os.path.join(download_folder,
|
||||||
_IMAGE_TO_EXTRACT),
|
_IMAGE_TO_EXTRACT),
|
||||||
@ -270,8 +265,13 @@ def RunAUTestHarness(board, channel, latest_url_base, zip_server_base,
|
|||||||
'--board=%s' % board,
|
'--board=%s' % board,
|
||||||
'--type=%s' % type,
|
'--type=%s' % type,
|
||||||
'--remote=%s' % remote,
|
'--remote=%s' % remote,
|
||||||
|
'--private_key=%s' % os.path.join(update_engine_path,
|
||||||
|
'unittest_key.pem'),
|
||||||
|
'--public_key=%s' % os.path.join(update_engine_path,
|
||||||
|
'unittest_key.pub.pem'),
|
||||||
]
|
]
|
||||||
if no_graphics: cmd.append('--no_graphics')
|
if no_graphics: cmd.append('--no_graphics')
|
||||||
|
if clean: cmd.append('--clean')
|
||||||
|
|
||||||
RunCommand(cmd, cwd=crosutils_root)
|
RunCommand(cmd, cwd=crosutils_root)
|
||||||
|
|
||||||
@ -299,25 +299,14 @@ def main():
|
|||||||
parser.set_usage(parser.format_help())
|
parser.set_usage(parser.format_help())
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
if args:
|
if args: parser.error('Extra args found %s.' % args)
|
||||||
parser.error('Extra args found %s.' % args)
|
if not options.board: parser.error('Need board for image to compare against.')
|
||||||
|
if not options.channel: parser.error('Need channel e.g. dev-channel.')
|
||||||
if not options.board:
|
if not options.zipbase: parser.error('Need zip url base to get images.')
|
||||||
parser.error('Need board for image to compare against.')
|
|
||||||
|
|
||||||
if not options.channel:
|
|
||||||
parser.error('Need channel for image to compare against.')
|
|
||||||
|
|
||||||
if not options.zipbase:
|
|
||||||
parser.error('Need zip url base to get images.')
|
|
||||||
|
|
||||||
if not options.cache:
|
|
||||||
Info('Wiping dev server cache.')
|
|
||||||
WipeDevServerCache()
|
|
||||||
|
|
||||||
RunAUTestHarness(options.board, options.channel, options.latestbase,
|
RunAUTestHarness(options.board, options.channel, options.latestbase,
|
||||||
options.zipbase, options.no_graphics, options.type,
|
options.zipbase, options.no_graphics, options.type,
|
||||||
options.remote)
|
options.remote, not options.cache)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -50,7 +50,7 @@ DEFINE_boolean factory_install $FLAGS_FALSE \
|
|||||||
|
|
||||||
# We default to TRUE so the buildbot gets its image. Note this is different
|
# We default to TRUE so the buildbot gets its image. Note this is different
|
||||||
# behavior from image_to_usb.sh
|
# behavior from image_to_usb.sh
|
||||||
DEFINE_boolean force_copy ${FLAGS_TRUE} "Always rebuild test image"
|
DEFINE_boolean force_copy ${FLAGS_FALSE} "Always rebuild test image"
|
||||||
DEFINE_string format "qemu" \
|
DEFINE_string format "qemu" \
|
||||||
"Output format, either qemu, vmware or virtualbox"
|
"Output format, either qemu, vmware or virtualbox"
|
||||||
DEFINE_string from "" \
|
DEFINE_string from "" \
|
||||||
|
Loading…
Reference in New Issue
Block a user