# Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Test overlay.""" from __future__ import absolute_import from __future__ import division from __future__ import print_function import os import shutil import subprocess import tempfile import unittest from . import config from . import overlay import re class BindOverlayTest(unittest.TestCase): def setUp(self): self.source_dir = tempfile.mkdtemp() self.destination_dir = tempfile.mkdtemp() # # base_dir/ # base_project/ # .git # no_git_dir/ # no_git_subdir1/ # no_git_file1 # no_git_subdir2/ # no_git_file2 # overlays/ # unittest1/ # from_dir/ # .git/ # upper_subdir/ # lower_subdir/ # from_unittest1/ # .git/ # from_file # unittest2/ # upper_subdir/ # lower_subdir/ # from_unittest2/ # .git/ # no_git_dir2/ # no_git_subdir1/ # no_git_subdir2/ # .bindmount # os.mkdir(os.path.join(self.source_dir, 'base_dir')) os.mkdir(os.path.join(self.source_dir, 'base_dir', 'base_project')) os.mkdir(os.path.join(self.source_dir, 'base_dir', 'base_project', '.git')) os.mkdir(os.path.join(self.source_dir, 'no_git_dir')) os.mkdir(os.path.join(self.source_dir, 'no_git_dir', 'no_git_subdir1')) open(os.path.join(self.source_dir, 'no_git_dir', 'no_git_subdir1', 'no_git_file1'), 'a').close() os.mkdir(os.path.join(self.source_dir, 'no_git_dir', 'no_git_subdir2')) open(os.path.join(self.source_dir, 'no_git_dir', 'no_git_subdir2', 'no_git_file2'), 'a').close() os.mkdir(os.path.join(self.source_dir, 'overlays')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'unittest1')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'unittest1', 'from_dir')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'unittest1', 'from_dir', '.git')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'unittest1', 'upper_subdir')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'unittest1', 'upper_subdir', 'lower_subdir')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'unittest1', 'upper_subdir', 'lower_subdir', 'from_unittest1')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'unittest1', 'upper_subdir', 'lower_subdir', 'from_unittest1', '.git')) os.symlink( os.path.join(self.source_dir, 'overlays', 'unittest1', 'upper_subdir', 'lower_subdir'), os.path.join(self.source_dir, 'overlays', 'unittest1', 'upper_subdir', 'subdir_symlink') ) open(os.path.join(self.source_dir, 'overlays', 'unittest1', 'from_file'), 'a').close() os.mkdir(os.path.join(self.source_dir, 'overlays', 'unittest2')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'unittest2', 'upper_subdir')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'unittest2', 'upper_subdir', 'lower_subdir')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'unittest2', 'upper_subdir', 'lower_subdir', 'from_unittest2')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'unittest2', 'upper_subdir', 'lower_subdir', 'from_unittest2', '.git')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'no_git_dir2')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'no_git_dir2', 'no_git_subdir1')) os.mkdir(os.path.join(self.source_dir, 'overlays', 'no_git_dir2', 'no_git_subdir2')) open(os.path.join(self.source_dir, 'overlays', 'no_git_dir2', 'no_git_subdir2', '.bindmount'), 'a').close() def tearDown(self): shutil.rmtree(self.source_dir) def testValidTargetOverlayBinds(self): with tempfile.NamedTemporaryFile('w+t') as test_config: test_config.write( '' '' ' ' ' ' ' ' ' ' ' ' ' ' '' ) test_config.flush() o = overlay.BindOverlay( cfg=config.factory(test_config.name), build_target='unittest', source_dir=self.source_dir) self.assertIsNotNone(o) bind_mounts = o.GetBindMounts() bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_dir') bind_destination = os.path.join(self.source_dir, 'from_dir') self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False)) self.assertIn(os.path.join(self.source_dir, 'base_dir', 'base_project'), bind_mounts) def testValidTargetOverlayBindsAllowedProjects(self): with tempfile.NamedTemporaryFile('w+t') as test_config, \ tempfile.NamedTemporaryFile('w+t') as test_allowed_projects: test_config.write( '' '' ' ' ' ' ' ' ' ' ' ' ' ' '' % test_allowed_projects.name ) test_config.flush() test_allowed_projects.write( '' '' ' ' '' ) test_allowed_projects.flush() o = overlay.BindOverlay( cfg=config.factory(test_config.name), build_target='unittest', source_dir=self.source_dir) self.assertIsNotNone(o) bind_mounts = o.GetBindMounts() self.assertIn(os.path.join(self.source_dir, 'from_dir'), bind_mounts) self.assertNotIn(os.path.join(self.source_dir, 'base_dir', 'base_project'), bind_mounts) def testMultipleOverlays(self): with tempfile.NamedTemporaryFile('w+t') as test_config: test_config.write( '' '' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '' ) test_config.flush() o = overlay.BindOverlay( cfg=config.factory(test_config.name), build_target='unittest', source_dir=self.source_dir) self.assertIsNotNone(o) bind_mounts = o.GetBindMounts() bind_source = os.path.join(self.source_dir, 'overlays/unittest1/upper_subdir/lower_subdir/from_unittest1') bind_destination = os.path.join(self.source_dir, 'upper_subdir/lower_subdir/from_unittest1') self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False)) bind_source = os.path.join(self.source_dir, 'overlays/unittest2/upper_subdir/lower_subdir/from_unittest2') bind_destination = os.path.join(self.source_dir, 'upper_subdir/lower_subdir/from_unittest2') self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False)) def testMultipleOverlaysWithAllowlist(self): with tempfile.NamedTemporaryFile('w+t') as test_config: test_config.write( '' '' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '' ) test_config.flush() o = overlay.BindOverlay( cfg=config.factory(test_config.name), build_target='unittest', source_dir=self.source_dir) self.assertIsNotNone(o) bind_mounts = o.GetBindMounts() bind_source = os.path.join(self.source_dir, 'overlays/unittest1/upper_subdir/lower_subdir/from_unittest1') bind_destination = os.path.join(self.source_dir, 'upper_subdir/lower_subdir/from_unittest1') self.assertEqual( bind_mounts[bind_destination], overlay.BindMount(source_dir=bind_source, readonly=False, allows_replacement=False)) bind_source = os.path.join(self.source_dir, 'overlays/unittest2/upper_subdir/lower_subdir/from_unittest2') bind_destination = os.path.join(self.source_dir, 'upper_subdir/lower_subdir/from_unittest2') self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False)) def testAllowReadWriteNoGitDir(self): with tempfile.NamedTemporaryFile('w+t') as test_config: test_config.write( '' '' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '' ) test_config.flush() o = overlay.BindOverlay( cfg=config.factory(test_config.name), build_target='unittest', source_dir=self.source_dir) self.assertIsNotNone(o) bind_mounts = o.GetBindMounts() bind_source = os.path.join(self.source_dir, 'no_git_dir/no_git_subdir1') bind_destination = os.path.join(self.source_dir, 'no_git_dir/no_git_subdir1') self.assertIn(bind_destination, bind_mounts) self.assertEqual( bind_mounts[bind_destination], overlay.BindMount(source_dir=bind_source, readonly=False, allows_replacement=False)) bind_source = os.path.join(self.source_dir, 'no_git_dir/no_git_subdir2') bind_destination = os.path.join(self.source_dir, 'no_git_dir/no_git_subdir2') self.assertIn(bind_destination, bind_mounts) self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False)) def testValidOverlaidDir(self): with tempfile.NamedTemporaryFile('w+t') as test_config: test_config.write( '' '' ' ' ' ' ' ' ' ' ' ' ' ' '' ) test_config.flush() o = overlay.BindOverlay( cfg=config.factory(test_config.name), build_target='unittest', source_dir=self.source_dir, destination_dir=self.destination_dir) self.assertIsNotNone(o) bind_mounts = o.GetBindMounts() bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_dir') bind_destination = os.path.join(self.destination_dir, 'from_dir') self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False)) def testValidFilesystemViewDirectoryBind(self): with tempfile.NamedTemporaryFile('w+t') as test_config: test_config.write( '' '' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '' ) test_config.flush() o = overlay.BindOverlay( cfg=config.factory(test_config.name), build_target='unittest', source_dir=self.source_dir) self.assertIsNotNone(o) bind_mounts = o.GetBindMounts() bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_dir') bind_destination = os.path.join(self.source_dir, 'to_dir') self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False)) def testValidFilesystemViewFileBind(self): with tempfile.NamedTemporaryFile('w+t') as test_config: test_config.write( '' '' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '' ) test_config.flush() o = overlay.BindOverlay( cfg=config.factory(test_config.name), build_target='unittest', source_dir=self.source_dir) self.assertIsNotNone(o) bind_mounts = o.GetBindMounts() bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_file') bind_destination = os.path.join(self.source_dir, 'to_file') self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False)) def testInvalidTarget(self): with tempfile.NamedTemporaryFile('w+t') as test_config: test_config.write( '' '' ' ' ' ' ' ' ' ' ' ' ' ' '' ) test_config.flush() with self.assertRaises(KeyError): overlay.BindOverlay( cfg=config.factory(test_config.name), build_target='unknown', source_dir=self.source_dir) def testExplicitBindMount(self): with tempfile.NamedTemporaryFile('w+t') as test_config: test_config.write( '' '' ' ' ' ' ' ' ' ' ' ' ' ' '' ) test_config.flush() o = overlay.BindOverlay( cfg=config.factory(test_config.name), build_target='target_name', source_dir=self.source_dir) self.assertIsNotNone(o) bind_mounts = o.GetBindMounts() bind_source = os.path.join(self.source_dir, 'overlays/no_git_dir2/no_git_subdir1') bind_destination = os.path.join(self.source_dir, 'no_git_subdir1') self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False)) bind_source = os.path.join(self.source_dir, 'overlays/no_git_dir2/no_git_subdir2') bind_destination = os.path.join(self.source_dir, 'no_git_subdir2') self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, False)) def testReplacementPath(self): with tempfile.NamedTemporaryFile('w+t') as test_config: test_config.write( '' '' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '' ) test_config.flush() o = overlay.BindOverlay( cfg=config.factory(test_config.name), build_target='unittest', source_dir=self.source_dir) self.assertIsNotNone(o) bind_mounts = o.GetBindMounts() bind_source = os.path.join(self.source_dir, 'overlays/unittest1/from_dir') bind_destination = os.path.join(self.source_dir, 'from_dir') self.assertEqual(bind_mounts[bind_destination], overlay.BindMount(bind_source, True, True)) if __name__ == '__main__': unittest.main()