Support for config files
authorMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Fri, 19 Apr 2013 13:44:52 +0000 (16:44 +0300)
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Tue, 23 Apr 2013 06:43:36 +0000 (09:43 +0300)
Initial support for reading service configuration from a file. For now,
there is only one configuration setting, i.e. 'repo-cache-dir'.
All configuration settings can be overridden with environment variables,
if needed.

By default, the service tries to read
'/etc/obs/services/git-buildpackage' and '~/.obs/git-buildpackage'.
Other file(s) can be defined with the --config cmdline option.

Change-Id: I65d5ee26b3798646e49615d98019673ce5606d5d
Signed-off-by: Markus Lehtonen <markus.lehtonen@linux.intel.com>
config/git-buildpackage [new file with mode: 0644]
obs_service_gbp/__init__.py
obs_service_gbp/command.py
packaging/obs-service-git-buildpackage.spec
setup.py
tests/test_obs_service_gbp.py

diff --git a/config/git-buildpackage b/config/git-buildpackage
new file mode 100644 (file)
index 0000000..31b9cfc
--- /dev/null
@@ -0,0 +1,10 @@
+##
+## Configuration for the git-buildpackage OBS source service
+##
+
+[general]
+## Repository cache directory.
+## Default is '/var/cache/obs/git-buildpackage-repos/'
+## You probably need to define if the service is run on the client side as
+## the default directory is likely not accessible by regular users.
+#repo-cache-dir = /var/cache/obs/git-buildpackage-repos/
index 4488122..dce8ec3 100644 (file)
@@ -89,8 +89,8 @@ class CachedRepoError(Exception):
 class CachedRepo(object):
     """Object representing a cached repository"""
 
-    def __init__(self, url, bare=False):
-        self.basedir = '/var/cache/obs/git-buildpackage-repos/'
+    def __init__(self, base_dir, url, bare=False):
+        self.basedir = base_dir
         self.repodir = None
         self.repo = None
         self.lock = None
@@ -100,8 +100,6 @@ class CachedRepo(object):
 
     def _init_cache_base(self):
         """Check and initialize repository cache base directory"""
-        if 'CACHEDIR' in os.environ:
-            self.basedir = os.environ['CACHEDIR']
         LOGGER.debug("Using cache basedir '%s'" % self.basedir)
         if not os.path.exists(self.basedir):
             LOGGER.debug('Creating missing cache basedir')
index 46178db..e36d096 100644 (file)
@@ -21,6 +21,7 @@
 import os
 import argparse
 
+from ConfigParser import SafeConfigParser
 from gbp.scripts.buildpackage_rpm import main as gbp_rpm
 
 from obs_service_gbp import LOGGER, gbplog, CachedRepo, CachedRepoError
@@ -43,9 +44,33 @@ def construct_gbp_args(args):
         argv.append('--git-spec-vcs-tag=%s' % args.spec_vcs_tag)
     return argv
 
+def read_config(filenames):
+    '''Read configuration file(s)'''
+    defaults = {'repo-cache-dir': '/var/cache/obs/git-buildpackage-repos/'}
+
+    filenames = [os.path.expanduser(fname) for fname in filenames]
+    LOGGER.debug('Trying %s config files: %s' % (len(filenames), filenames))
+    parser = SafeConfigParser(defaults=defaults)
+    read = parser.read(filenames)
+    LOGGER.debug('Read %s config files: %s' % (len(read), read))
+
+    # Add our one-and-only section, if it does not exist
+    if not parser.has_section('general'):
+        parser.add_section('general')
+
+    # Read overrides from environment
+    for key in defaults.keys():
+        envvar ='OBS_GIT_BUILDPACKAGE_%s' % key.replace('-', '_').upper()
+        if envvar in os.environ:
+            parser.set('general', key, os.environ[envvar])
+
+    # We only use keys from one section, for now
+    return dict(parser.items('general'))
 
 def parse_args(argv):
     """Argument parser"""
+    default_configs = ['/etc/obs/services/git-buildpackage',
+                       '~/.obs/git-buildpackage']
 
     parser = argparse.ArgumentParser()
     parser.add_argument('--url', help='Remote repository URL', required=True)
@@ -56,21 +81,25 @@ def parse_args(argv):
                         choices=['yes', 'no'])
     parser.add_argument('--spec-vcs-tag', help='Set/update the VCS tag in the'
                                                'spec file')
+    parser.add_argument('--config', default=default_configs, action='append',
+                        help='Config file to use, can be given multiple times')
     return parser.parse_args(argv)
 
 def main(argv=None):
     """Main function"""
 
-    LOGGER.info('Starting git-buildpackage source service')
     args = parse_args(argv)
 
+    LOGGER.info('Starting git-buildpackage source service')
     if args.verbose == 'yes':
         gbplog.setup(color='auto', verbose=True)
         LOGGER.setLevel(gbplog.DEBUG)
 
+    config = read_config(args.config)
+
     # Create / update cached repository
     try:
-        repo = CachedRepo(args.url)
+        repo = CachedRepo(config['repo-cache-dir'], args.url)
         args.revision = repo.update_working_copy(args.revision)
     except CachedRepoError as err:
         LOGGER.error('RepoCache: %s' % str(err))
@@ -78,12 +107,16 @@ def main(argv=None):
 
     # Export sources with GBP
     gbp_args = construct_gbp_args(args)
-    os.chdir(repo.repodir)
-    LOGGER.info('Exporting packaging files with GBP')
-    ret = gbp_rpm(gbp_args)
+    orig_dir = os.path.abspath(os.curdir)
+    try:
+        os.chdir(repo.repodir)
+        LOGGER.info('Exporting packaging files with GBP')
+        ret = gbp_rpm(gbp_args)
+    finally:
+        os.chdir(orig_dir)
     if ret:
         LOGGER.error('Git-buildpackage-rpm failed, unable to export packaging '
-                     'files')
+                 'files')
         return 2
 
     return 0
index 12585b6..f056d2d 100644 (file)
@@ -51,3 +51,6 @@ rm -rf %{buildroot}%{python_sitelib}/*info
 %dir /usr/lib/obs/service
 /usr/lib/obs/service/*
 %{python_sitelib}/obs_service_gbp
+%dir %{_sysconfdir}/obs
+%dir %{_sysconfdir}/obs/services
+%config %{_sysconfdir}/obs/services/*
index f424ae2..a9cea8a 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -37,5 +37,6 @@ setup(name='obs_service_gbp',
       author_email='markus.lehtonen@linux.intel.com',
       packages=['obs_service_gbp'],
       data_files=[('/usr/lib/obs/service', ['service/git-buildpackage',
-                    'service/git-buildpackage.service'])],
+                    'service/git-buildpackage.service']),
+                  ('/etc/obs/services', ['config/git-buildpackage'])],
      )
index 1857420..8b1b19b 100644 (file)
@@ -88,7 +88,7 @@ class UnitTestsBase(object):
         # Use cache in our tmpdir
         suffix = os.path.basename(self.tmpdir).replace('test', '')
         self.cachedir = os.path.join(self.workdir, 'cache' + suffix)
-        os.environ['CACHEDIR'] = self.cachedir
+        os.environ['OBS_GIT_BUILDPACKAGE_REPO_CACHE_DIR'] = self.cachedir
         # Create temporary "orig" repository
         repo_dir = os.path.join(self.workdir, 'orig' + suffix)
         shutil.copytree(self._template_repo.path, repo_dir)
@@ -150,6 +150,23 @@ class TestBasicFunctionality(UnitTestsBase):
         assert service(['--url', self.orig_repo.path,
                         '--spec-vcs-tag=orig/%(tagname)s']) == 0
 
+    def test_options_config(self):
+        """Test the --config option"""
+        # Create config file
+        with open('my.conf', 'w') as conf:
+            conf.write('[general]\n')
+            conf.write('repo-cache-dir = my-repo-cache\n')
+
+        # Mangle environment
+        default_cache = os.environ['OBS_GIT_BUILDPACKAGE_REPO_CACHE_DIR']
+        del os.environ['OBS_GIT_BUILDPACKAGE_REPO_CACHE_DIR']
+
+        # Check that the repo cache we configured is actually used
+        assert (service(['--url', self.orig_repo.path, '--config', 'my.conf'])
+                == 0)
+        assert not os.path.exists(default_cache), os.listdir('.')
+        assert os.path.exists('my-repo-cache'), os.listdir('.')
+
 
 class TestObsRepoGitRepository(UnitTestsBase):
     """Test the special GitRepository class"""
@@ -163,28 +180,33 @@ class TestObsRepoGitRepository(UnitTestsBase):
         repo.set_config('foo.bar', 'bax', replace=True)
         assert repo.get_config('foo.bar') == 'bax'
 
+
 class TestCachedRepo(UnitTestsBase):
     """Test CachedRepo class"""
 
+    def MockCachedRepo(self, url, **kwargs):
+        """Automatically use suitable cache dir"""
+        return CachedRepo(self.cachedir, url, **kwargs)
+
     def test_invalid_url(self):
         """Test invalid url"""
         with assert_raises(CachedRepoError):
-            CachedRepo('foo/bar.git')
+            self.MockCachedRepo('foo/bar.git')
         with assert_raises(CachedRepoError):
-            CachedRepo('foo/baz.git', bare=True)
+            self.MockCachedRepo('foo/baz.git', bare=True)
 
         # Try updating from non-existing repo
-        repo = CachedRepo(self.orig_repo.path)
+        repo = self.MockCachedRepo(self.orig_repo.path)
         del repo
         shutil.move(self.orig_repo.path, self.orig_repo.path + '.tmp')
         with assert_raises(CachedRepoError):
-            repo = CachedRepo(self.orig_repo.path)
+            repo = self.MockCachedRepo(self.orig_repo.path)
         shutil.move(self.orig_repo.path + '.tmp', self.orig_repo.path)
 
     def test_clone_and_fetch(self):
         """Basic test for cloning and fetching"""
         # Clone
-        repo = CachedRepo(self.orig_repo.path)
+        repo = self.MockCachedRepo(self.orig_repo.path)
         assert repo
         assert repo.repo.bare is not True
         sha = repo.repo.rev_parse('master')
@@ -193,14 +215,14 @@ class TestCachedRepo(UnitTestsBase):
         # Make new commit in "upstream"
         self.update_repository_file(self.orig_repo, 'foo.txt', 'more data\n')
         # Fetch
-        repo = CachedRepo(self.orig_repo.path)
+        repo = self.MockCachedRepo(self.orig_repo.path)
         assert repo
         assert path == repo.repo.path
         assert sha != repo.repo.rev_parse('master')
 
     def test_update_working_copy(self):
         """Test update functionality"""
-        repo = CachedRepo(self.orig_repo.path)
+        repo = self.MockCachedRepo(self.orig_repo.path)
         # Check that the refs are mapped correctly
         sha = repo.update_working_copy('HEAD~1')
         assert sha == self.orig_repo.rev_parse('HEAD~1')
@@ -218,25 +240,25 @@ class TestCachedRepo(UnitTestsBase):
         shas = [self.orig_repo.rev_parse('HEAD~2'),
                 self.orig_repo.rev_parse('HEAD~1'),
                 self.orig_repo.rev_parse('HEAD')]
-        repo = CachedRepo(self.orig_repo.path)
+        repo = self.MockCachedRepo(self.orig_repo.path)
         repo.update_working_copy(shas[-1])
         del repo
 
         # Change upstream, after this index cached repo will be out-of-sync
         # from orig HEAD
         self.orig_repo.set_branch('HEAD~1')
-        repo = CachedRepo(self.orig_repo.path)
+        repo = self.MockCachedRepo(self.orig_repo.path)
         assert repo.update_working_copy(shas[0]) == shas[0]
 
     def test_update_bare(self):
         """Test update for bare repository"""
-        repo = CachedRepo(self.orig_repo.path, bare=True)
+        repo = self.MockCachedRepo(self.orig_repo.path, bare=True)
         with assert_raises(CachedRepoError):
             repo.update_working_copy('HEAD')
 
     def test_invalid_remote_head(self):
         """Test clone/update from remote whose HEAD is invalid"""
-        repo = CachedRepo(self.orig_repo.path)
+        repo = self.MockCachedRepo(self.orig_repo.path)
         del repo
 
         # Make remote HEAD point to a non-existent branch
@@ -244,7 +266,7 @@ class TestCachedRepo(UnitTestsBase):
         with open(os.path.join(self.orig_repo.git_dir, 'HEAD'), 'w') as head:
             head.write('ref: refs/heads/non-existent-branch\n')
 
-        repo = CachedRepo(self.orig_repo.path)
+        repo = self.MockCachedRepo(self.orig_repo.path)
         # Local HEAD should be invalid, now
         with assert_raises(CachedRepoError):
             repo.update_working_copy('HEAD')
@@ -257,23 +279,23 @@ class TestCachedRepo(UnitTestsBase):
     def test_corrupted_cache(self):
         """Test recovering from corrupted cache"""
         # Clone
-        repo = CachedRepo(self.orig_repo.path)
+        repo = self.MockCachedRepo(self.orig_repo.path)
         # Corrupt repo
         shutil.rmtree(os.path.join(repo.repo.path, '.git/refs'))
         with assert_raises(GitRepositoryError):
             repo.repo.rev_parse('HEAD')
         del repo
         # Update and check status
-        repo = CachedRepo(self.orig_repo.path)
+        repo = self.MockCachedRepo(self.orig_repo.path)
         assert repo.repo.rev_parse('HEAD')
 
     def test_changing_repotype(self):
         """Test changing repo type from bare -> normal"""
         # Clone
-        repo = CachedRepo(self.orig_repo.path, bare=True)
+        repo = self.MockCachedRepo(self.orig_repo.path, bare=True)
         assert repo.repo.bare == True
         del repo
-        repo = CachedRepo(self.orig_repo.path, bare=False)
+        repo = self.MockCachedRepo(self.orig_repo.path, bare=False)
         assert repo.repo.bare == False
 
     def test_cache_access_error(self):
@@ -281,23 +303,23 @@ class TestCachedRepo(UnitTestsBase):
         # Check base cachedir creation access error
         os.chmod(self.workdir, 0)
         with assert_raises(CachedRepoError):
-            repo = CachedRepo(self.orig_repo.path)
+            repo = self.MockCachedRepo(self.orig_repo.path)
         os.chmod(self.workdir, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)
-        repo = CachedRepo(self.orig_repo.path)
+        repo = self.MockCachedRepo(self.orig_repo.path)
         del repo
 
         # Check cache base dir access error
         os.chmod(self.cachedir, 0)
         with assert_raises(CachedRepoError):
-            repo = CachedRepo(self.orig_repo.path)
+            repo = self.MockCachedRepo(self.orig_repo.path)
         os.chmod(self.cachedir, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)
-        repo = CachedRepo(self.orig_repo.path)
+        repo = self.MockCachedRepo(self.orig_repo.path)
         del repo
 
         # Check repodir delete eror
         os.chmod(self.cachedir, stat.S_IREAD | stat.S_IEXEC)
         with assert_raises(CachedRepoError):
             # Change repo type -> tries to delete
-            repo = CachedRepo(self.orig_repo.path, bare=True)
+            repo = self.MockCachedRepo(self.orig_repo.path, bare=True)
         os.chmod(self.cachedir, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)