| #!/usr/bin/python2 |
| # Copyright 2017 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. |
| |
| import tempfile |
| import unittest |
| |
| import common |
| from autotest_lib.client.bin import utils |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.site_utils import lxc |
| from autotest_lib.site_utils.lxc import base_image |
| from autotest_lib.site_utils.lxc import unittest_setup |
| from autotest_lib.site_utils.lxc import utils as lxc_utils |
| |
| |
| class ContainerFactoryTests(lxc_utils.LXCTests): |
| """Unit tests for the ContainerFactory class.""" |
| |
| @classmethod |
| def setUpClass(cls): |
| super(ContainerFactoryTests, cls).setUpClass() |
| cls.test_dir = tempfile.mkdtemp(dir=lxc.DEFAULT_CONTAINER_PATH, |
| prefix='container_factory_unittest_') |
| |
| # Check if a base container exists on this machine and download one if |
| # necessary. |
| image = base_image.BaseImage(lxc.DEFAULT_CONTAINER_PATH, lxc.BASE) |
| try: |
| cls.base_container = image.get() |
| cls.cleanup_base_container = False |
| except error.ContainerError: |
| image.setup() |
| cls.base_container = image.get() |
| cls.cleanup_base_container = True |
| assert(cls.base_container is not None) |
| |
| @classmethod |
| def tearDownClass(cls): |
| cls.base_container = None |
| if not unittest_setup.config.skip_cleanup: |
| if cls.cleanup_base_container: |
| image = base_image.BaseImage(lxc.DEFAULT_CONTAINER_PATH, |
| lxc.BASE) |
| image.cleanup() |
| utils.run('sudo rm -r %s' % cls.test_dir) |
| |
| def setUp(self): |
| # Create a separate dir for each test, so they are hermetic. |
| self.test_dir = tempfile.mkdtemp(dir=ContainerFactoryTests.test_dir) |
| self.test_factory = lxc.ContainerFactory( |
| base_container=self.base_container, |
| lxc_path=self.test_dir) |
| |
| def testCreateContainer(self): |
| """Tests basic container creation.""" |
| container = self.test_factory.create_container() |
| |
| try: |
| container.refresh_status() |
| except: |
| self.fail('Invalid container:\n%s' % error.format_error()) |
| |
| def testCreateContainer_noId(self): |
| """Tests container creation with default IDs.""" |
| container = self.test_factory.create_container() |
| self.assertIsNone(container.id) |
| |
| def testCreateContainer_withId(self): |
| """Tests container creation with given IDs. """ |
| id0 = lxc.ContainerId(1, 2, 3) |
| container = self.test_factory.create_container(id0) |
| self.assertEquals(id0, container.id) |
| |
| def testContainerName(self): |
| """Tests that created containers have the right name.""" |
| id0 = lxc.ContainerId(1, 2, 3) |
| id1 = lxc.ContainerId(42, 41, 40) |
| |
| container0 = self.test_factory.create_container(id0) |
| container1 = self.test_factory.create_container(id1) |
| |
| self.assertEqual(str(id0), container0.name) |
| self.assertEqual(str(id1), container1.name) |
| |
| def testContainerPath(self): |
| """Tests that created containers have the right LXC path.""" |
| dir0 = tempfile.mkdtemp(dir=self.test_dir) |
| dir1 = tempfile.mkdtemp(dir=self.test_dir) |
| |
| container0 = self.test_factory.create_container(lxc_path=dir0) |
| container1 = self.test_factory.create_container(lxc_path=dir1) |
| |
| self.assertEqual(dir0, container0.container_path) |
| self.assertEqual(dir1, container1.container_path) |
| |
| def testCreateContainer_alreadyExists(self): |
| """Tests that container ID conflicts raise errors as expected.""" |
| id0 = lxc.ContainerId(1, 2, 3) |
| |
| self.test_factory.create_container(id0) |
| with self.assertRaises(error.ContainerError): |
| self.test_factory.create_container(id0) |
| |
| def testCreateContainer_forceReset(self): |
| """Tests that force-resetting containers works.""" |
| factory = lxc.ContainerFactory(base_container=self.base_container, |
| lxc_path=self.test_dir, |
| force_cleanup=True) |
| |
| id0 = lxc.ContainerId(1, 2, 3) |
| container0 = factory.create_container(id0) |
| container0.start(wait_for_network=False) |
| |
| # Create a file in the original container. |
| tmpfile = container0.attach_run('mktemp').stdout |
| exists = 'test -e %s' % tmpfile |
| try: |
| container0.attach_run(exists) |
| except error.CmdError as e: |
| self.fail(e) |
| |
| # Create a new container in place of the original, then verify that the |
| # file is no longer there. |
| container1 = factory.create_container(id0) |
| container1.start(wait_for_network=False) |
| with self.assertRaises(error.CmdError): |
| container1.attach_run(exists) |
| |
| def testCreateContainer_subclass(self): |
| """Tests that the factory produces objects of the requested class.""" |
| container = self.test_factory.create_container() |
| # Don't use isinstance, we want to check the exact type. |
| self.assertTrue(type(container) is lxc.Container) |
| |
| class _TestContainer(lxc.Container): |
| """A test Container subclass""" |
| pass |
| |
| test_factory = lxc.ContainerFactory(base_container=self.base_container, |
| container_class=_TestContainer, |
| lxc_path=self.test_dir) |
| test_container = test_factory.create_container() |
| self.assertTrue(type(test_container) is _TestContainer) |
| |
| def testCreateContainer_snapshotFails(self): |
| """Tests the scenario where snapshotting fails. |
| |
| Verifies that the factory is still able to produce a Container when |
| cloning fails. |
| """ |
| class MockContainerClass(object): |
| """A mock object to simulate the container class. |
| |
| This mock has a clone method that simulates a failure when clone is |
| called with snapshot=True. Clone calls are recorded so they can be |
| verified later. |
| """ |
| |
| def __init__(self): |
| """Initializes the mock.""" |
| self.clone_count = 0 |
| self.clone_kwargs = [] |
| |
| def clone(self, *args, **kwargs): |
| """Mocks the Container.clone class method. """ |
| # Record the particulars of this call. |
| self.clone_count += 1 |
| self.clone_kwargs.append(kwargs) |
| # Simulate failure if a snapshot is requested, otherwise create |
| # and return the clone. |
| if kwargs['snapshot']: |
| raise error.CmdError('fake error', None) |
| else: |
| return lxc.Container.clone(*args, **kwargs) |
| |
| mock = MockContainerClass() |
| factory = lxc.ContainerFactory(base_container=self.base_container, |
| container_class=mock, |
| snapshot=True, |
| lxc_path=self.test_dir) |
| |
| factory.create_container() |
| # The factory should have made 2 calls to mock.clone - the first with |
| # snapshot=True, then the second with snapshot=False. |
| self.assertEquals(2, mock.clone_count) |
| self.assertTrue(mock.clone_kwargs[0]['snapshot']) |
| self.assertFalse(mock.clone_kwargs[1]['snapshot']) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |