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