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