diff options
author | Sam Thursfield <sam.thursfield@codethink.co.uk> | 2014-05-06 16:03:53 +0000 |
---|---|---|
committer | Sam Thursfield <sam.thursfield@codethink.co.uk> | 2014-05-08 09:28:39 +0000 |
commit | 8a52526219c4ba07eec0ba83566fa8e5a5debd88 (patch) | |
tree | 8eeacb23bfc6de26dc42b05169d09663e190f822 | |
parent | 9ea146fdcfbe9b93b297165b1b5222e1eadb9d23 (diff) | |
download | morph-8a52526219c4ba07eec0ba83566fa8e5a5debd88.tar.gz |
Add 'morph validate' command to run 'git fsck' in all cached repos
-rw-r--r-- | morphlib/gitdir.py | 9 | ||||
-rw-r--r-- | morphlib/localrepocache.py | 26 | ||||
-rw-r--r-- | morphlib/plugins/cache_check_plugin.py | 73 | ||||
-rw-r--r-- | morphlib/plugins/validate_plugin.py | 46 | ||||
-rw-r--r-- | without-test-modules | 1 |
5 files changed, 154 insertions, 1 deletions
diff --git a/morphlib/gitdir.py b/morphlib/gitdir.py index 8f6d69d7..17aa4401 100644 --- a/morphlib/gitdir.py +++ b/morphlib/gitdir.py @@ -651,6 +651,15 @@ class GitDirectory(object): def get_relpath(self, path): # pragma: no cover return os.path.relpath(path, self.dirname) + def fsck(self): + fsck_command = ['git', 'fsck', '--no-dangling'] + status, output, errors = self._runcmd_unchecked(fsck_command) + + if status != 0: + return errors + else: + return None + def init(dirname): '''Initialise a new git repository.''' diff --git a/morphlib/localrepocache.py b/morphlib/localrepocache.py index 9c20e4bc..c34c7e12 100644 --- a/morphlib/localrepocache.py +++ b/morphlib/localrepocache.py @@ -1,4 +1,4 @@ -# Copyright (C) 2012-2013 Codethink Limited +# Copyright (C) 2012-2014 Codethink Limited # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -235,3 +235,27 @@ class LocalRepoCache(object): cached_repo = self.get_repo(reponame) return cached_repo + def validate(self): + '''Validate all cached repos. + + Returns a dictionary mapping path -> errors for any locally cached repo + for which `git fsck` reported an error. + ''' + + errors_dict = {} + + for repo_dir in os.listdir(self._cachedir): + repo_path = os.path.join(self._cachedir, repo_dir) + self._app.status(msg='Checking cached git repository %s' % + repo_dir, chatty=True) + + gitdir = morphlib.gitdir.GitDirectory(repo_path) + error_text = gitdir.fsck() + + if error_text is not None: + logging.error( + 'Corruption found in locally cached git repo at %s: %s' % + (repo_dir, error_text)) + errors_dict[repo_path] = error_text + + return errors_dict diff --git a/morphlib/plugins/cache_check_plugin.py b/morphlib/plugins/cache_check_plugin.py new file mode 100644 index 00000000..3a3487f2 --- /dev/null +++ b/morphlib/plugins/cache_check_plugin.py @@ -0,0 +1,73 @@ +# Copyright (C) 2014 Codethink Limited +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +import cliapp +import contextlib +import uuid + +import morphlib + + +class CacheCheckPlugin(cliapp.Plugin): + '''Check for corruption in Morph's inputs and caches. + + This plugin is intended to be useful when investigating unexplained errors + during builds or in built systems. If the inputs or caches contained + corruption, this may explain the errors. + + Note that this does not check the 'ccache' data. There is no way to + validate if this has been corrupted, currently. You can disable ccache in + Morph with the 'no-ccache' setting. + + ''' + + def enable(self): + self.app.add_subcommand('check-git-cache', self.check_git_cache) + self.app.add_subcommand('check-artifact-cache', + self.check_artifact_cache) + + def disable(self): + pass + + def check_git_cache(self, args): + '''Check for corruption in the local cache of Git repositories.''' + + lrc, rrc = morphlib.util.new_repo_caches(self.app) + + # Trove is ignored -- validating that is really up to the sysadmin. + # morph could do that, though. + + git_errors = lrc.validate() + + self.app.output.write('Found corruption in %i cached git repos.\n' % + len(git_errors)) + + for repo_dir, error_text in git_errors.iteritems(): + self.app.output.write(' %s\n' % repo_dir) + if self.app.settings['verbose']: + error_text_indented = '\n'.join( + [' ' % line for line in error_text.split('\n')]) + self.app.output.write(" %s\n" % error_text_indented) + + def check_artifact_cache(self, args): + '''Check for corruption in the local cache of built artifacts. + + This includes the artifact cache itself, and the unpacked chunk + artifacts which are used at build time. + + ''' + lac, rac = morphlib.util.new_artifact_caches(self.app.settings) + lac.validate() diff --git a/morphlib/plugins/validate_plugin.py b/morphlib/plugins/validate_plugin.py new file mode 100644 index 00000000..5234f100 --- /dev/null +++ b/morphlib/plugins/validate_plugin.py @@ -0,0 +1,46 @@ +# Copyright (C) 2012,2013,2014 Codethink Limited +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +import cliapp +import contextlib +import uuid + +import morphlib + + +class ValidatePlugin(cliapp.Plugin): + + def enable(self): + self.app.add_subcommand('validate', self.validate) + + def validate(self, args): + '''Validate inputs and caches. + + ''' + + lrc, rrc = morphlib.util.new_repo_caches(self.app) + + git_errors = lrc.validate() + + self.app.output.write('Found corruption in %i cached git repos.\n' % + len(git_errors)) + + for repo_dir, error_text in git_errors.iteritems(): + self.app.output.write(' %s\n' % repo_dir) + if self.app.settings['verbose']: + error_text_indented = '\n'.join( + [' ' + line for line in error_text.split('\n')]) + self.app.output.write(" %s\n" % error_text_indented) diff --git a/without-test-modules b/without-test-modules index a42cce97..e746194c 100644 --- a/without-test-modules +++ b/without-test-modules @@ -33,5 +33,6 @@ morphlib/plugins/print_architecture_plugin.py morphlib/plugins/add_binary_plugin.py morphlib/plugins/push_pull_plugin.py morphlib/plugins/distbuild_plugin.py +morphlib/plugins/cache_check_plugin.py # Not unit tested, since it needs a full system branch morphlib/buildbranch.py |