diff --git a/Atomic/backends/_docker.py b/Atomic/backends/_docker.py index 2f609162..e6cad50b 100644 --- a/Atomic/backends/_docker.py +++ b/Atomic/backends/_docker.py @@ -399,8 +399,6 @@ def uninstall(self, iobject, name=None, **kwargs): if cmd: return util.check_call(cmd, env=atomic.cmd_env()) - # Delete the entry in the install data - util.InstallData.delete_by_id(iobject.id, ignore=ignore) return self.delete_image(iobject.image, force=args.force) @@ -457,10 +455,6 @@ def add_string_or_list_to_list(list_item, value): else: return self._start(iobject, args, atomic) - if iobject.get_label('INSTALL') and not args.ignore and not util.InstallData.image_installed(iobject): - raise ValueError("The image '{}' appears to have not been installed and has an INSTALL label. You " - "should install this image first. Re-run with --ignore to bypass this " - "error.".format(iobject.name or iobject.image)) # The object is an image command = [] if iobject.run_command: diff --git a/Atomic/install.py b/Atomic/install.py index 572f77d3..dc0f5729 100644 --- a/Atomic/install.py +++ b/Atomic/install.py @@ -135,11 +135,6 @@ def install(self): name = img_obj.fq_name except RegistryInspectError: name = img_obj.input_name - install_data = {} - install_data[name] = {'id': img_obj.id, - 'install_date': strftime("%Y-%m-%d %H:%M:%S", gmtime()) - } - util.InstallData.write_install_data(install_data) return util.check_call(cmd) @staticmethod diff --git a/Atomic/util.py b/Atomic/util.py index a3dfecc5..84d3c294 100644 --- a/Atomic/util.py +++ b/Atomic/util.py @@ -6,6 +6,7 @@ import json import subprocess import collections +from contextlib import contextmanager from fnmatch import fnmatch as matches import os import selinux @@ -31,10 +32,6 @@ ATOMIC_CONF = os.environ.get('ATOMIC_CONF', '/etc/atomic.conf') ATOMIC_CONFD = os.environ.get('ATOMIC_CONFD', '/etc/atomic.d/') ATOMIC_LIBEXEC = os.environ.get('ATOMIC_LIBEXEC', '/usr/libexec/atomic') -ATOMIC_VAR_LIB = os.environ.get('ATOMIC_VAR_LIB', '/var/lib/atomic') -if not os.path.exists(ATOMIC_VAR_LIB): - os.makedirs(ATOMIC_VAR_LIB) -ATOMIC_INSTALL_JSON = os.environ.get('ATOMIC_INSTALL_JSON', os.path.join(ATOMIC_VAR_LIB, 'install.json')) GOMTREE_PATH = "/usr/bin/gomtree" BWRAP_OCI_PATH = "/usr/bin/bwrap-oci" @@ -761,116 +758,6 @@ def load_scan_result_file(file_name): return json.loads(open(os.path.join(file_name), "r").read()) -def file_lock(func): - lock_file_name = "{}.lock".format(os.path.join(os.path.dirname(ATOMIC_INSTALL_JSON), "." + os.path.basename(ATOMIC_INSTALL_JSON))) - - # Create the temporary lockfile if it doesn't exist - if not os.path.exists(lock_file_name): - open(lock_file_name, 'a').close() - - install_data_file = open(lock_file_name, "r") - - def get_lock(): - ''' - Obtains a read-only file lock on the install data - :return: - ''' - time_out = 0 - f_lock = False - while time_out < 10.5: # Ten second attempt to get a lock - try: - fcntl.flock(install_data_file, fcntl.LOCK_EX | fcntl.LOCK_NB) - f_lock = True - break - except IOError: - time.sleep(.5) - time_out += .5 - if not f_lock: - raise ValueError("Unable to get file lock for {}".format(ATOMIC_INSTALL_JSON)) - - def release_lock(): - fcntl.flock(install_data_file, fcntl.LOCK_UN) - - def wrapper(*args, **kwargs): - get_lock() - ret = func(*args, **kwargs) - release_lock() - return ret - - return wrapper - - -class InstallData(object): - if not os.path.exists(ATOMIC_INSTALL_JSON): - open(ATOMIC_INSTALL_JSON, 'a').close() - - install_file_handle = open(ATOMIC_INSTALL_JSON, 'r') - - @staticmethod - def _read_install_data(file_handle): - try: - return json.loads(file_handle.read()) - except ValueError: - return {} - - @classmethod - def _write_install_data(cls, new_data): - install_data = cls._read_install_data(cls.install_file_handle) - for x in new_data: - install_data[x] = new_data[x] - temp_file = tempfile.NamedTemporaryFile(mode='w', delete=False) - json.dump(install_data, temp_file) - temp_file.close() - shutil.move(temp_file.name, ATOMIC_INSTALL_JSON) - - @classmethod - @file_lock - def read_install_data(cls): - if os.path.exists(ATOMIC_INSTALL_JSON): - read_data = cls._read_install_data(cls.install_file_handle) - return read_data - return {} - - @classmethod - @file_lock - def write_install_data(cls, new_data): - cls._write_install_data(new_data) - - @classmethod - def get_install_name_by_id(cls, iid, install_data=None): - if not install_data: - install_data = cls._read_install_data(cls.install_file_handle) - for installed_image in install_data: - if install_data[installed_image]['id'] == iid: - return installed_image - raise ValueError("Unable to find {} in installed image data ({}). Re-run command with -i to ignore".format(id, ATOMIC_INSTALL_JSON)) - - @classmethod - @file_lock - def delete_by_id(cls, iid, ignore=False): - install_data = cls._read_install_data(cls.install_file_handle) - try: - id_key = InstallData.get_install_name_by_id(iid, install_data=install_data) - except ValueError as e: - if not ignore: - raise ValueError(str(e)) - return - del install_data[id_key] - return cls._write_install_data(install_data) - - @classmethod - def image_installed(cls, img_object): - install_data = cls.read_install_data() - if install_data.get(img_object.id, None): - return True - if install_data.get(img_object.input_name, None): - return True - if install_data.get(img_object.name, None): - return True - if install_data.get(img_object.image, None): - return True - return False - class Decompose(object): """ Class for decomposing an input string in its respective parts like registry, diff --git a/tests/unit/test_util.py b/tests/unit/test_util.py index ea40a8b7..370f555d 100644 --- a/tests/unit/test_util.py +++ b/tests/unit/test_util.py @@ -155,93 +155,5 @@ def reset_data(cls): def grow_data(cls, var_name, name): cls.install_data[name] = getattr(cls, var_name) -local_centos_inspect = {'Id': '16e9fdecc1febc87fb1ca09271009cf5f28eb8d4aec5515922ef298c145a6726', 'RepoDigests': ['docker.io/centos@sha256:7793b39617b28c6cd35774c00383b89a1265f3abf6efcaf0b8f4aafe4e0662d2'], 'Parent': '', 'GraphDriver': {'Name': 'devicemapper', 'Data': {'DeviceSize': '10737418240', 'DeviceName': 'docker-253:2-5900125-3fb5b406e6a53142129237c9e2c3a1ce8b6cf269b5f8071fcd62107c41544cd2', 'DeviceId': '779'}}, 'Created': '2016-08-30T18:20:19.39890162Z', 'Comment': '', 'DockerVersion': '1.12.1', 'VirtualSize': 210208812, 'Author': 'The CentOS Project - ami_creator', 'Os': 'linux', 'RootFS': {'Type': 'layers', 'Layers': ['5fa0fa02637842ab1ddc8b3a17b86691c87c87d20800e6a95a113343f6ffd84c']}, 'Container': 'a5b0819aa82c224095e1a18e9df0776a7b38d32bacca073f054723b65fb54f0e', 'Architecture': 'amd64', 'RepoTags': ['docker.io/centos:centos7.0.1406'], 'Config': {'Labels': {}, 'Entrypoint': None, 'StdinOnce': False, 'OnBuild': None, 'Env': ['PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'], 'Volumes': None, 'Cmd': None, 'User': '', 'AttachStdin': False, 'AttachStderr': False, 'AttachStdout': False, 'WorkingDir': '', 'Tty': False, 'Image': '20ae10d641a0af6f25ceaa75fdcf591d171e3c521a54a3f3a2868b602d735e11', 'Hostname': 'a5b0819aa82c', 'Domainname': '', 'OpenStdin': False}, 'Size': 210208812, 'ContainerConfig': {'Labels': {}, 'Entrypoint': None, 'StdinOnce': False, 'OnBuild': None, 'Env': ['PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'], 'Volumes': None, 'Cmd': ['/bin/sh', '-c', '#(nop) ADD file:6a409eac27f0c7e04393da096dbeff01b929405e79b15222a0dc06a2084d3df3 in / '], 'User': '', 'AttachStdin': False, 'AttachStderr': False, 'AttachStdout': False, 'WorkingDir': '', 'Tty': False, 'Image': '20ae10d641a0af6f25ceaa75fdcf591d171e3c521a54a3f3a2868b602d735e11', 'Hostname': 'a5b0819aa82c', 'Domainname': '', 'OpenStdin': False}} -rhel_docker_inspect = {u'Comment': u'', u'Container': u'', u'DockerVersion': u'1.9.1', u'Parent': u'', u'Created': u'2016-10-26T12:02:33.368772Z', u'Config': {u'Tty': False, u'Cmd': [u'/bin/bash'], u'Volumes': None, u'Domainname': u'', u'WorkingDir': u'', u'Image': u'f6f6121b053b2312688c87d3a1d32d06a984dc01d2ea7738508a50581cddb6b4', u'Hostname': u'', u'StdinOnce': False, u'Labels': {u'com.redhat.component': u'rhel-server-docker', u'authoritative-source-url': u'registry.access.redhat.com', u'distribution-scope': u'public', u'Vendor': u'Red Hat, Inc.', u'Name': u'rhel7/rhel', u'Build_Host': u'rcm-img01.build.eng.bos.redhat.com', u'vcs-type': u'git', u'name': u'rhel7/rhel', u'vcs-ref': u'7eeaf203cf909c2c056fba7066db9c1073a28d97', u'release': u'45', u'Version': u'7.3', u'Architecture': u'x86_64', u'version': u'7.3', u'Release': u'45', u'vendor': u'Red Hat, Inc.', u'BZComponent': u'rhel-server-docker', u'build-date': u'2016-10-26T07:54:17.037911Z', u'com.redhat.build-host': u'ip-10-29-120-48.ec2.internal', u'architecture': u'x86_64'}, u'AttachStdin': False, u'User': u'', u'Env': [u'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', u'container=docker'], u'Entrypoint': None, u'OnBuild': [], u'AttachStderr': False, u'AttachStdout': False, u'OpenStdin': False}, u'Author': u'Red Hat, Inc.', u'GraphDriver': {u'Data': {u'DeviceName': u'docker-253:2-5900125-a2bce97a4fd7ea12dce9865caa461ead8d1caf51ef452aba2f1b9d98efdf968f', u'DeviceSize': u'10737418240', u'DeviceId': u'623'}, u'Name': u'devicemapper'}, u'VirtualSize': 192508958, u'Os': u'linux', u'Architecture': u'amd64', u'RootFS': {u'Layers': [u'34d3e0e77091d9d51c6f70a7a7a4f7536aab214a55e02a8923af8f80cbe60d30', u'ccd6fc81ec49bd45f04db699401eb149b1945bb7292476b390ebdcdd7d975697'], u'Type': u'layers'}, u'ContainerConfig': {u'Tty': False, u'Cmd': None, u'Volumes': None, u'Domainname': u'', u'WorkingDir': u'', u'Image': u'', u'Hostname': u'', u'StdinOnce': False, u'Labels': None, u'AttachStdin': False, u'User': u'', u'Env': None, u'Entrypoint': None, u'OnBuild': None, u'AttachStderr': False, u'AttachStdout': False, u'OpenStdin': False}, u'Size': 192508958, u'RepoDigests': [u'registry.access.redhat.com/rhel7@sha256:da8a3e9297da7ccd1948366103d13c45b7e77489382351a777a7326004b63a21'], u'Id': u'f98706e16e41e56c4beaeea9fa77cd00fe35693635ed274f128876713afc0a1e', u'RepoTags': [u'registry.access.redhat.com/rhel7:latest']} - - -@unittest.skipIf(no_mock, "Mock not found") -class InstallData(unittest.TestCase): - - class Args(): - def __init__(self): - self.storage = None - self.debug = False - self.name = None - self.image = None - - @patch('Atomic.util.InstallData.read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData._read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData.write_install_data', new=MockIO.write_mock) - def test_read(self): - MockIO.reset_data() - self.assertEqual(util.InstallData.read_install_data(), MockIO.install_data) - - @patch('Atomic.util.InstallData.read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData._read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData.write_install_data', new=MockIO.write_mock) - def test_write(self): - MockIO.reset_data() - install_data = util.InstallData.read_install_data() - install_data['docker.io/library/centos:latest'] = MockIO.new_data_fq - util.InstallData.write_install_data(install_data) - self.assertTrue('docker.io/library/centos:latest' in util.InstallData.read_install_data()) - - @patch('Atomic.util.InstallData.read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData._read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData.write_install_data', new=MockIO.write_mock) - def test_get_install_name_by_id(self): - MockIO.reset_data() - MockIO.grow_data('new_data_fq', 'docker.io/library/centos:latest') - self.assertEqual(util.InstallData.get_install_name_by_id('16e9fdecc1febc87fb1ca09271009cf5f28eb8d4aec5515922ef298c145a6726', install_data=MockIO.install_data), 'docker.io/library/centos:latest') - - @patch('Atomic.util.InstallData.read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData._read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData.write_install_data', new=MockIO.write_mock) - def test_fail_get_install_name_by_id(self): - MockIO.reset_data() - self.assertRaises(ValueError, util.InstallData.get_install_name_by_id, 1, MockIO.install_data) - - @patch('Atomic.util.InstallData.read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData._read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData.write_install_data', new=MockIO.write_mock) - def test_image_installed_name(self): - MockIO.reset_data() - MockIO.grow_data('new_data_fq', 'docker.io/library/centos:latest') - args = self.Args() - args.storage = 'docker' - args.image = 'docker.io/library/centos:latest' - db = DockerBackend() - db._inspect_image = MagicMock(return_value=local_centos_inspect) - local_image_object = db.inspect_image(args.image) - self.assertTrue(util.InstallData.image_installed(local_image_object)) - - @patch('Atomic.util.InstallData.read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData._read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData.write_install_data', new=MockIO.write_mock) - def test_image_installed_id(self): - MockIO.reset_data() - MockIO.grow_data('new_data_fq', '16e9fdecc1febc87fb1ca09271009cf5f28eb8d4aec5515922ef298c145a6726') - args = self.Args() - args.storage = 'docker' - args.image = 'docker.io/library/centos:latest' - db = DockerBackend() - db._inspect_image = MagicMock(return_value=local_centos_inspect) - local_image_object = db.inspect_image(args.image) - self.assertTrue(util.InstallData.image_installed(local_image_object)) - - @patch('Atomic.util.InstallData.read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData._read_install_data', new=MockIO.read_mock) - @patch('Atomic.util.InstallData.write_install_data', new=MockIO.write_mock) - def test_image_not_installed(self): - MockIO.reset_data() - args = self.Args() - args.storage = 'docker' - args.image = 'registry.access.redhat.com/rhel7' - db = DockerBackend() - db._inspect_image = MagicMock(return_value=rhel_docker_inspect) - local_image_object = db.inspect_image(args.image) - self.assertFalse(util.InstallData.image_installed(local_image_object)) - - if __name__ == '__main__': unittest.main()