• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python2
2# Copyright 2017 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import os
7import shutil
8import tempfile
9import unittest
10from contextlib import contextmanager
11
12import common
13from autotest_lib.client.common_lib import error
14from autotest_lib.site_utils import lxc
15from autotest_lib.site_utils.lxc import constants
16from autotest_lib.site_utils.lxc import unittest_setup
17from autotest_lib.site_utils.lxc import utils as lxc_utils
18from autotest_lib.site_utils.lxc.base_image import BaseImage
19
20
21test_dir = None
22# A reference to an existing base container that can be copied for tests that
23# need a base container.  This is an optimization.
24reference_container = None
25# The reference container can either be a reference to an existing container, or
26# to a container that was downloaded by this test.  If the latter, then it needs
27# to be cleaned up when the tests are complete.
28cleanup_ref_container = False
29
30
31class BaseImageTests(lxc_utils.LXCTests):
32    """Unit tests to verify the BaseImage class."""
33
34    def testCreate_existing(self):
35        """Verifies that BaseImage works with existing base containers."""
36        with TestBaseContainer() as control:
37            manager = BaseImage(control.container_path, control.name)
38            self.assertIsNotNone(manager.base_container)
39            self.assertEquals(control.container_path,
40                              manager.base_container.container_path)
41            self.assertEquals(control.name, manager.base_container.name)
42            try:
43                manager.base_container.refresh_status()
44            except error.ContainerError:
45                self.fail('Base container was not valid.\n%s' %
46                          error.format_error())
47
48    def testCleanup_noClones(self):
49        """Verifies that cleanup cleans up the base image."""
50        base = lxc.Container.clone(src=reference_container,
51                                   new_name=constants.BASE,
52                                   new_path=test_dir,
53                                   snapshot=True)
54
55        manager = BaseImage(base.container_path, base.name)
56        # Precondition: ensure base exists and is a valid container.
57        base.refresh_status()
58
59        manager.cleanup()
60
61        # Verify that the base container was cleaned up.
62        self.assertFalse(lxc_utils.path_exists(
63            os.path.join(base.container_path, base.name)))
64
65    def testCleanup_withClones(self):
66        """Verifies that cleanup cleans up the base image.
67
68        Ensure that it works even when clones of the base image exist.
69        """
70        # Do not snapshot, as snapshots of snapshots behave differently than
71        # snapshots of full container clones.  BaseImage cleanup code assumes
72        # that the base container is not a snapshot.
73        base = lxc.Container.clone(src=reference_container,
74                                   new_name=constants.BASE,
75                                   new_path=test_dir,
76                                   snapshot=False)
77        manager = BaseImage(base.container_path, base.name)
78        clones = []
79        for i in range(3):
80            clones.append(lxc.Container.clone(src=base,
81                                              new_name='clone_%d' % i,
82                                              snapshot=True))
83
84        # Precondition: all containers are valid.
85        base.refresh_status()
86        for container in clones:
87            container.refresh_status()
88
89        manager.cleanup()
90
91        # Verify that all containers were cleaned up
92        self.assertFalse(lxc_utils.path_exists(
93            os.path.join(base.container_path, base.name)))
94        for container in clones:
95            if constants.SUPPORT_SNAPSHOT_CLONE:
96                # Snapshot clones should get deleted along with the base
97                # container.
98                self.assertFalse(lxc_utils.path_exists(
99                    os.path.join(container.container_path, container.name)))
100            else:
101                # If snapshot clones aren't supported (e.g. on moblab), the
102                # clones should not be affected by the destruction of the base
103                # container.
104                try:
105                    container.refresh_status()
106                except error.ContainerError:
107                    self.fail(error.format_error())
108
109
110class BaseImageSetupTests(lxc_utils.LXCTests):
111    """Unit tests to verify the setup of specific images.
112
113    Some images differ in layout from others.  These tests test specific images
114    to make sure the setup code works for all of them.
115    """
116
117    def setUp(self):
118        self.manager = BaseImage(test_dir, lxc.BASE)
119
120    def tearDown(self):
121        self.manager.cleanup()
122
123    def testSetupBase05(self):
124        """Verifies that setup works for moblab base container.
125
126        Verifies that the code for installing the rootfs location into the
127        lxc config, is working correctly.
128        """
129        # Set up the bucket, then start the base container, and verify it works.
130        self.manager.setup('base_05')
131        container = self.manager.base_container
132
133        container.start(wait_for_network=False)
134        self.assertTrue(container.is_running())
135
136    @unittest.skipIf(constants.IS_MOBLAB,
137                     "Moblab does not support the regular base container.")
138    def testSetupBase09(self):
139        """Verifies that setup works for base container.
140
141        Verifies that the code for installing the rootfs location into the
142        lxc config, is working correctly.
143        """
144        self.manager.setup('base_09')
145        container = self.manager.base_container
146
147        container.start(wait_for_network=False)
148        self.assertTrue(container.is_running())
149
150
151@contextmanager
152def TestBaseContainer(name=constants.BASE):
153    """Context manager for creating a scoped base container for testing.
154
155    @param name: (optional) Name of the base container.  If this is not
156                 provided, the default base container name is used.
157    """
158    container = lxc.Container.clone(src=reference_container,
159                                    new_name=name,
160                                    new_path=test_dir,
161                                    snapshot=True,
162                                    cleanup=False)
163    try:
164        yield container
165    finally:
166        if not unittest_setup.config.skip_cleanup:
167            container.destroy()
168
169
170def setUpModule():
171    """Module setup for base image unittests.
172
173    Sets up a test directory along with a reference container that is used by
174    tests that need an existing base container.
175    """
176    global test_dir
177    global reference_container
178    global cleanup_ref_container
179
180    test_dir = tempfile.mkdtemp(dir=lxc.DEFAULT_CONTAINER_PATH,
181                                prefix='base_container_manager_unittest_')
182    # Unfortunately, aside from duping the BaseImage code completely, there
183    # isn't an easy way to download and configure a base container.  So even
184    # though this is the BaseImage unittest, we use a BaseImage to set it up.
185    bcm = BaseImage(lxc.DEFAULT_CONTAINER_PATH, lxc.BASE)
186    if bcm.base_container is None:
187        bcm.setup()
188        cleanup_ref_container = True
189    reference_container = bcm.base_container
190
191
192def tearDownModule():
193    """Deletes the test dir and reference container."""
194    if not unittest_setup.config.skip_cleanup:
195        if cleanup_ref_container:
196            reference_container.destroy()
197        shutil.rmtree(test_dir)
198
199
200if __name__ == '__main__':
201    unittest.main()
202