summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Thursfield <sam.thursfield@codethink.co.uk>2014-05-06 16:03:53 +0000
committerSam Thursfield <sam.thursfield@codethink.co.uk>2014-05-08 09:28:39 +0000
commit8a52526219c4ba07eec0ba83566fa8e5a5debd88 (patch)
tree8eeacb23bfc6de26dc42b05169d09663e190f822
parent9ea146fdcfbe9b93b297165b1b5222e1eadb9d23 (diff)
downloadmorph-8a52526219c4ba07eec0ba83566fa8e5a5debd88.tar.gz
Add 'morph validate' command to run 'git fsck' in all cached repos
-rw-r--r--morphlib/gitdir.py9
-rw-r--r--morphlib/localrepocache.py26
-rw-r--r--morphlib/plugins/cache_check_plugin.py73
-rw-r--r--morphlib/plugins/validate_plugin.py46
-rw-r--r--without-test-modules1
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