Commit f1a9b11c authored by Łukasz Nowak's avatar Łukasz Nowak

kvm: Fix test for boot images

The test have incorrect leftover of old limited implementation, now the system
shall update images by itself.

Also the typical scenario - image on first request - was not covered.

Adapt the test to code without needed linking.
parent ecc36da6
Pipeline #19960 passed with stage
in 0 seconds
...@@ -36,7 +36,6 @@ import requests ...@@ -36,7 +36,6 @@ import requests
import six import six
import slapos.util import slapos.util
import sqlite3 import sqlite3
import stat
from six.moves.urllib.parse import parse_qs, urlparse from six.moves.urllib.parse import parse_qs, urlparse
import unittest import unittest
import subprocess import subprocess
...@@ -752,49 +751,61 @@ class FakeImageHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): ...@@ -752,49 +751,61 @@ class FakeImageHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
class FakeImageServerMixin(KvmMixin): class FakeImageServerMixin(KvmMixin):
def startImageHttpServer(self): @classmethod
self.image_source_directory = tempfile.mkdtemp() def startImageHttpServer(cls):
cls.image_source_directory = tempfile.mkdtemp()
server = SocketServer.TCPServer( server = SocketServer.TCPServer(
(self._ipv4_address, findFreeTCPPort(self._ipv4_address)), (cls._ipv4_address, findFreeTCPPort(cls._ipv4_address)),
FakeImageHandler) FakeImageHandler)
# c89f17758be13adeb06886ef935d5ff1
fake_image_content = b'fake_image_content' fake_image_content = b'fake_image_content'
self.fake_image_md5sum = hashlib.md5(fake_image_content).hexdigest() cls.fake_image_md5sum = hashlib.md5(fake_image_content).hexdigest()
with open(os.path.join( with open(os.path.join(
self.image_source_directory, self.fake_image_md5sum), 'wb') as fh: cls.image_source_directory, cls.fake_image_md5sum), 'wb') as fh:
fh.write(fake_image_content) fh.write(fake_image_content)
# bc81d2aee81e030c6cee210c802339c2
fake_image2_content = b'fake_image2_content' fake_image2_content = b'fake_image2_content'
self.fake_image2_md5sum = hashlib.md5(fake_image2_content).hexdigest() cls.fake_image2_md5sum = hashlib.md5(fake_image2_content).hexdigest()
with open(os.path.join( with open(os.path.join(
self.image_source_directory, self.fake_image2_md5sum), 'wb') as fh: cls.image_source_directory, cls.fake_image2_md5sum), 'wb') as fh:
fh.write(fake_image2_content) fh.write(fake_image2_content)
self.fake_image_wrong_md5sum = self.fake_image2_md5sum cls.fake_image_wrong_md5sum = cls.fake_image2_md5sum
# c5ef5d70ad5a0dbfd890a734f588e344
fake_image3_content = b'fake_image3_content'
cls.fake_image3_md5sum = hashlib.md5(fake_image3_content).hexdigest()
with open(os.path.join(
cls.image_source_directory, cls.fake_image3_md5sum), 'wb') as fh:
fh.write(fake_image3_content)
url = 'http://%s:%s' % server.server_address url = 'http://%s:%s' % server.server_address
self.fake_image = '/'.join([url, self.fake_image_md5sum]) cls.fake_image = '/'.join([url, cls.fake_image_md5sum])
self.fake_image2 = '/'.join([url, self.fake_image2_md5sum]) cls.fake_image2 = '/'.join([url, cls.fake_image2_md5sum])
cls.fake_image3 = '/'.join([url, cls.fake_image3_md5sum])
old_dir = os.path.realpath(os.curdir) old_dir = os.path.realpath(os.curdir)
os.chdir(self.image_source_directory) os.chdir(cls.image_source_directory)
try: try:
self.server_process = multiprocessing.Process( cls.server_process = multiprocessing.Process(
target=server.serve_forever, name='FakeImageHttpServer') target=server.serve_forever, name='FakeImageHttpServer')
self.server_process.start() cls.server_process.start()
finally: finally:
os.chdir(old_dir) os.chdir(old_dir)
def stopImageHttpServer(self): @classmethod
self.logger.debug('Stopping process %s' % (self.server_process,)) def stopImageHttpServer(cls):
self.server_process.join(10) cls.logger.debug('Stopping process %s' % (cls.server_process,))
self.server_process.terminate() cls.server_process.join(10)
cls.server_process.terminate()
time.sleep(0.1) time.sleep(0.1)
if self.server_process.is_alive(): if cls.server_process.is_alive():
self.logger.warning( cls.logger.warning(
'Process %s still alive' % (self.server_process, )) 'Process %s still alive' % (cls.server_process, ))
shutil.rmtree(self.image_source_directory) shutil.rmtree(cls.image_source_directory)
@skipUnlessKvm @skipUnlessKvm
...@@ -805,6 +816,7 @@ class TestBootImageUrlList(InstanceTestCase, FakeImageServerMixin): ...@@ -805,6 +816,7 @@ class TestBootImageUrlList(InstanceTestCase, FakeImageServerMixin):
# variations # variations
key = 'boot-image-url-list' key = 'boot-image-url-list'
test_input = "%s#%s\n%s#%s" test_input = "%s#%s\n%s#%s"
empty_input = ""
image_directory = 'boot-image-url-list-repository' image_directory = 'boot-image-url-list-repository'
config_state_promise = 'boot-image-url-list-config-state-promise.py' config_state_promise = 'boot-image-url-list-config-state-promise.py'
download_md5sum_promise = 'boot-image-url-list-download-md5sum-promise.py' download_md5sum_promise = 'boot-image-url-list-download-md5sum-promise.py'
...@@ -830,12 +842,21 @@ class TestBootImageUrlList(InstanceTestCase, FakeImageServerMixin): ...@@ -830,12 +842,21 @@ class TestBootImageUrlList(InstanceTestCase, FakeImageServerMixin):
@classmethod @classmethod
def getInstanceParameterDict(cls): def getInstanceParameterDict(cls):
# start with empty, but working configuration return {
return {} cls.key: cls.test_input % (
cls.fake_image, cls.fake_image_md5sum, cls.fake_image2,
cls.fake_image2_md5sum)
}
def setUp(self): @classmethod
super(InstanceTestCase, self).setUp() def setUpClass(cls):
self.startImageHttpServer() cls.startImageHttpServer()
super(InstanceTestCase, cls).setUpClass()
@classmethod
def tearDownClass(cls):
super(InstanceTestCase, cls).tearDownClass()
cls.stopImageHttpServer()
def tearDown(self): def tearDown(self):
# clean up the instance for other tests # clean up the instance for other tests
...@@ -845,7 +866,6 @@ class TestBootImageUrlList(InstanceTestCase, FakeImageServerMixin): ...@@ -845,7 +866,6 @@ class TestBootImageUrlList(InstanceTestCase, FakeImageServerMixin):
# 2nd ...move instance to "default" state # 2nd ...move instance to "default" state
self.rerequestInstance({}) self.rerequestInstance({})
self.slap.waitForInstance(max_retry=10) self.slap.waitForInstance(max_retry=10)
self.stopImageHttpServer()
super(InstanceTestCase, self).tearDown() super(InstanceTestCase, self).tearDown()
def getRunningImageList(self, kvm_instance_partition, def getRunningImageList(self, kvm_instance_partition,
...@@ -862,8 +882,6 @@ class TestBootImageUrlList(InstanceTestCase, FakeImageServerMixin): ...@@ -862,8 +882,6 @@ class TestBootImageUrlList(InstanceTestCase, FakeImageServerMixin):
m = _match_cdrom(entry) m = _match_cdrom(entry)
if m: if m:
path = m.group(1) path = m.group(1)
st = os.stat(path)
if stat.S_ISREG(st.st_mode) and st.st_size:
image_list.append( image_list.append(
_sub_iso(r'\1-${ver}\3', _sub_iso(r'\1-${ver}\3',
sub_shared(r'${shared}/', sub_shared(r'${shared}/',
...@@ -872,47 +890,48 @@ class TestBootImageUrlList(InstanceTestCase, FakeImageServerMixin): ...@@ -872,47 +890,48 @@ class TestBootImageUrlList(InstanceTestCase, FakeImageServerMixin):
return image_list return image_list
def test(self): def test(self):
partition_parameter_kw = { # check that image is correctly downloaded
self.key: self.test_input % (
self.fake_image, self.fake_image_md5sum, self.fake_image2,
self.fake_image2_md5sum)
}
self.rerequestInstance(partition_parameter_kw)
self.slap.waitForInstance(max_retry=10)
# check that image is correctly downloaded and linked
kvm_instance_partition = os.path.join( kvm_instance_partition = os.path.join(
self.slap.instance_directory, self.kvm_instance_partition_reference) self.slap.instance_directory, self.kvm_instance_partition_reference)
image_repository = os.path.join( image_repository = os.path.join(
kvm_instance_partition, 'srv', self.image_directory) kvm_instance_partition, 'srv', self.image_directory)
image = os.path.join(image_repository, self.fake_image_md5sum) image = os.path.join(image_repository, self.fake_image_md5sum)
image_link = os.path.join(image_repository, 'image_001')
self.assertTrue(os.path.exists(image)) self.assertTrue(os.path.exists(image))
with open(image, 'rb') as fh: with open(image, 'rb') as fh:
image_md5sum = hashlib.md5(fh.read()).hexdigest() image_md5sum = hashlib.md5(fh.read()).hexdigest()
self.assertEqual(image_md5sum, self.fake_image_md5sum) self.assertEqual(image_md5sum, self.fake_image_md5sum)
self.assertTrue(os.path.islink(image_link))
self.assertEqual(os.readlink(image_link), image)
image2 = os.path.join(image_repository, self.fake_image2_md5sum) image2 = os.path.join(image_repository, self.fake_image2_md5sum)
image2_link = os.path.join(image_repository, 'image_002')
self.assertTrue(os.path.exists(image2)) self.assertTrue(os.path.exists(image2))
with open(image2, 'rb') as fh: with open(image2, 'rb') as fh:
image2_md5sum = hashlib.md5(fh.read()).hexdigest() image2_md5sum = hashlib.md5(fh.read()).hexdigest()
self.assertEqual(image2_md5sum, self.fake_image2_md5sum) self.assertEqual(image2_md5sum, self.fake_image2_md5sum)
self.assertTrue(os.path.islink(image2_link))
self.assertEqual(os.readlink(image2_link), image2)
# mimic the requirement: restart the instance by requesting it stopped and self.assertEqual(
# then started started, like user have to do it [
self.rerequestInstance(partition_parameter_kw, state='stopped') '${inst}/srv/%s/%s' % (self.image_directory, self.fake_image_md5sum),
self.slap.waitForInstance(max_retry=1) '${inst}/srv/%s/%s' % (self.image_directory, self.fake_image2_md5sum),
self.rerequestInstance(partition_parameter_kw, state='started') '${shared}/debian-${ver}-amd64-netinst.iso',
self.slap.waitForInstance(max_retry=3) ],
self.getRunningImageList(kvm_instance_partition)
)
# Switch image
self.rerequestInstance({
self.key: self.test_input % (
self.fake_image3, self.fake_image3_md5sum,
self.fake_image2, self.fake_image2_md5sum)
})
self.slap.waitForInstance(max_retry=10)
self.assertTrue(os.path.exists(os.path.join(
image_repository, self.fake_image3_md5sum)))
self.assertTrue(os.path.exists(os.path.join(
image_repository, self.fake_image2_md5sum)))
self.assertEqual( self.assertEqual(
[ [
'${inst}/srv/%s/image_001' % self.image_directory, '${inst}/srv/%s/%s' % (self.image_directory, self.fake_image3_md5sum),
'${inst}/srv/%s/image_002' % self.image_directory, '${inst}/srv/%s/%s' % (self.image_directory, self.fake_image2_md5sum),
'${shared}/debian-${ver}-amd64-netinst.iso', '${shared}/debian-${ver}-amd64-netinst.iso',
], ],
self.getRunningImageList(kvm_instance_partition) self.getRunningImageList(kvm_instance_partition)
...@@ -920,21 +939,16 @@ class TestBootImageUrlList(InstanceTestCase, FakeImageServerMixin): ...@@ -920,21 +939,16 @@ class TestBootImageUrlList(InstanceTestCase, FakeImageServerMixin):
# cleanup of images works, also asserts that configuration changes are # cleanup of images works, also asserts that configuration changes are
# reflected # reflected
partition_parameter_kw[self.key] = '' # Note: key is left and empty_input is provided, as otherwise the part
self.rerequestInstance(partition_parameter_kw) # which generate images is simply removed, which can lead to
self.slap.waitForInstance(max_retry=2) # leftover
self.rerequestInstance({self.key: self.empty_input})
self.slap.waitForInstance(max_retry=10)
self.assertEqual( self.assertEqual(
os.listdir(image_repository), os.listdir(image_repository),
[] []
) )
# mimic the requirement: restart the instance by requesting it stopped and
# then started started, like user have to do it
self.rerequestInstance(partition_parameter_kw, state='stopped')
self.slap.waitForInstance(max_retry=1)
self.rerequestInstance(partition_parameter_kw, state='started')
self.slap.waitForInstance(max_retry=3)
# again only default image is available in the running process # again only default image is available in the running process
self.assertEqual( self.assertEqual(
['${shared}/debian-${ver}-amd64-netinst.iso'], ['${shared}/debian-${ver}-amd64-netinst.iso'],
...@@ -1019,6 +1033,7 @@ class TestBootImageUrlSelect(TestBootImageUrlList): ...@@ -1019,6 +1033,7 @@ class TestBootImageUrlSelect(TestBootImageUrlList):
# variations # variations
key = 'boot-image-url-select' key = 'boot-image-url-select'
test_input = '["%s#%s", "%s#%s"]' test_input = '["%s#%s", "%s#%s"]'
empty_input = '[]'
image_directory = 'boot-image-url-select-repository' image_directory = 'boot-image-url-select-repository'
config_state_promise = 'boot-image-url-select-config-state-promise.py' config_state_promise = 'boot-image-url-select-config-state-promise.py'
download_md5sum_promise = 'boot-image-url-select-download-md5sum-promise.py' download_md5sum_promise = 'boot-image-url-select-download-md5sum-promise.py'
...@@ -1054,35 +1069,27 @@ class TestBootImageUrlSelect(TestBootImageUrlList): ...@@ -1054,35 +1069,27 @@ class TestBootImageUrlSelect(TestBootImageUrlList):
} }
self.rerequestInstance(partition_parameter_kw) self.rerequestInstance(partition_parameter_kw)
self.slap.waitForInstance(max_retry=10) self.slap.waitForInstance(max_retry=10)
# check that image is correctly downloaded and linked # check that image is correctly downloaded
for image_directory in [ for image_directory in [
'boot-image-url-list-repository', 'boot-image-url-select-repository']: 'boot-image-url-list-repository', 'boot-image-url-select-repository']:
image_repository = os.path.join( image_repository = os.path.join(
self.slap.instance_directory, self.kvm_instance_partition_reference, self.slap.instance_directory, self.kvm_instance_partition_reference,
'srv', image_directory) 'srv', image_directory)
image = os.path.join(image_repository, self.fake_image_md5sum) image = os.path.join(image_repository, self.fake_image_md5sum)
image_link = os.path.join(image_repository, 'image_001')
self.assertTrue(os.path.exists(image)) self.assertTrue(os.path.exists(image))
with open(image, 'rb') as fh: with open(image, 'rb') as fh:
image_md5sum = hashlib.md5(fh.read()).hexdigest() image_md5sum = hashlib.md5(fh.read()).hexdigest()
self.assertEqual(image_md5sum, self.fake_image_md5sum) self.assertEqual(image_md5sum, self.fake_image_md5sum)
self.assertTrue(os.path.islink(image_link))
self.assertEqual(os.readlink(image_link), image)
kvm_instance_partition = os.path.join( kvm_instance_partition = os.path.join(
self.slap.instance_directory, self.kvm_instance_partition_reference) self.slap.instance_directory, self.kvm_instance_partition_reference)
# mimic the requirement: restart the instance by requesting it stopped and
# then started started, like user have to do it
self.rerequestInstance(partition_parameter_kw, state='stopped')
self.slap.waitForInstance(max_retry=1)
self.rerequestInstance(partition_parameter_kw, state='started')
self.slap.waitForInstance(max_retry=3)
self.assertEqual( self.assertEqual(
[ [
'${inst}/srv/boot-image-url-select-repository/image_001', '${inst}/srv/boot-image-url-select-repository/%s' % (
'${inst}/srv/boot-image-url-list-repository/image_001', self.fake_image_md5sum,),
'${inst}/srv/boot-image-url-list-repository/%s' % (
self.fake_image_md5sum,),
'${shared}/debian-${ver}-amd64-netinst.iso', '${shared}/debian-${ver}-amd64-netinst.iso',
], ],
self.getRunningImageList(kvm_instance_partition) self.getRunningImageList(kvm_instance_partition)
...@@ -1113,13 +1120,6 @@ class TestBootImageUrlSelect(TestBootImageUrlList): ...@@ -1113,13 +1120,6 @@ class TestBootImageUrlSelect(TestBootImageUrlList):
[] []
) )
# mimic the requirement: restart the instance by requesting it stopped and
# then started started, like user have to do it
self.rerequestInstance(partition_parameter_kw, state='stopped')
self.slap.waitForInstance(max_retry=1)
self.rerequestInstance(partition_parameter_kw, state='started')
self.slap.waitForInstance(max_retry=3)
# again only default image is available in the running process # again only default image is available in the running process
self.assertEqual( self.assertEqual(
['${shared}/debian-${ver}-amd64-netinst.iso'], ['${shared}/debian-${ver}-amd64-netinst.iso'],
...@@ -1486,7 +1486,7 @@ class TestImageDownloadController(InstanceTestCase, FakeImageServerMixin): ...@@ -1486,7 +1486,7 @@ class TestImageDownloadController(InstanceTestCase, FakeImageServerMixin):
'destination-tmp': 'tmp', 'destination-tmp': 'tmp',
'url': self.fake_image, 'url': self.fake_image,
'destination': 'destination', 'destination': 'destination',
'link': 'image_001', 'image-number': '001',
'gzipped': False, 'gzipped': False,
'md5sum': self.fake_image_md5sum, 'md5sum': self.fake_image_md5sum,
} }
...@@ -1501,12 +1501,10 @@ class TestImageDownloadController(InstanceTestCase, FakeImageServerMixin): ...@@ -1501,12 +1501,10 @@ class TestImageDownloadController(InstanceTestCase, FakeImageServerMixin):
INF: Storing errors in %(error_state_file)s INF: Storing errors in %(error_state_file)s
INF: %(fake_image)s : Downloading INF: %(fake_image)s : Downloading
INF: %(fake_image)s : Stored with checksum %(checksum)s INF: %(fake_image)s : Stored with checksum %(checksum)s
INF: %(fake_image)s : Symlinking %(symlink)s -> %(destination)s
""".strip() % { """.strip() % {
'fake_image': self.fake_image, 'fake_image': self.fake_image,
'checksum': self.fake_image_md5sum, 'checksum': self.fake_image_md5sum,
'error_state_file': self.error_state_file, 'error_state_file': self.error_state_file,
'symlink': os.path.join(self.destination_directory, 'image_001'),
'destination': os.path.join(self.destination_directory, 'destination'), 'destination': os.path.join(self.destination_directory, 'destination'),
}) })
) )
...@@ -1533,7 +1531,6 @@ INF: %(fake_image)s : already downloaded ...@@ -1533,7 +1531,6 @@ INF: %(fake_image)s : already downloaded
'fake_image': self.fake_image, 'fake_image': self.fake_image,
'checksum': self.fake_image_md5sum, 'checksum': self.fake_image_md5sum,
'error_state_file': self.error_state_file, 'error_state_file': self.error_state_file,
'symlink': os.path.join(self.destination_directory, 'image_001'),
'destination': os.path.join(self.destination_directory, 'destination'), 'destination': os.path.join(self.destination_directory, 'destination'),
}) })
) )
...@@ -1548,7 +1545,7 @@ INF: %(fake_image)s : already downloaded ...@@ -1548,7 +1545,7 @@ INF: %(fake_image)s : already downloaded
'destination-tmp': 'tmp', 'destination-tmp': 'tmp',
'url': self.fake_image, 'url': self.fake_image,
'destination': 'destination', 'destination': 'destination',
'link': 'image_001', 'image-number': '001',
'gzipped': False, 'gzipped': False,
'md5sum': self.fake_image_wrong_md5sum, 'md5sum': self.fake_image_wrong_md5sum,
} }
...@@ -1566,7 +1563,6 @@ INF: %(fake_image)s : Downloading ...@@ -1566,7 +1563,6 @@ INF: %(fake_image)s : Downloading
""". strip() % { """. strip() % {
'fake_image': self.fake_image, 'fake_image': self.fake_image,
'error_state_file': self.error_state_file, 'error_state_file': self.error_state_file,
'symlink': os.path.join(self.destination_directory, 'image_001'),
'destination': os.path.join( 'destination': os.path.join(
self.destination_directory, 'destination'), self.destination_directory, 'destination'),
}) })
...@@ -1602,7 +1598,6 @@ INF: Storing errors in %(error_state_file)s ...@@ -1602,7 +1598,6 @@ INF: Storing errors in %(error_state_file)s
""". strip() % { """. strip() % {
'fake_image': self.fake_image, 'fake_image': self.fake_image,
'error_state_file': self.error_state_file, 'error_state_file': self.error_state_file,
'symlink': os.path.join(self.destination_directory, 'image_001'),
'destination': os.path.join( 'destination': os.path.join(
self.destination_directory, 'destination'), self.destination_directory, 'destination'),
}) })
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment