mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 00:55:19 +00:00
add hg_pillar and corresponding unit test
This commit is contained in:
parent
a52ebdac3b
commit
ca4061b5b8
130
salt/pillar/hg_pillar.py
Normal file
130
salt/pillar/hg_pillar.py
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014 Floris Bruynooghe <flub@devork.be>
|
||||||
|
|
||||||
|
"""Use remote Mercurial repository as a Pillar source
|
||||||
|
|
||||||
|
The module depends on the ``hglib`` python module being available.
|
||||||
|
This is the same requirement as for hgfs_ so should not pose any extra
|
||||||
|
hurdles.
|
||||||
|
|
||||||
|
This external Pillar source can be configued in the master config file as such:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
ext_pillar:
|
||||||
|
- hg: ssh://hg@example.co/user/repo
|
||||||
|
"""
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
import salt.pillar
|
||||||
|
|
||||||
|
try:
|
||||||
|
import hglib
|
||||||
|
except ImportError:
|
||||||
|
hglib = None
|
||||||
|
|
||||||
|
|
||||||
|
__virtualname__ = 'hg'
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# The default option values
|
||||||
|
__opts__ = {}
|
||||||
|
|
||||||
|
|
||||||
|
def __virtual__():
|
||||||
|
"""Only load if hglib is available"""
|
||||||
|
ext_pillar_sources = [x for x in __opts__.get('ext_pillar', [])]
|
||||||
|
if not any(['hg' in x for x in ext_pillar_sources]):
|
||||||
|
return False
|
||||||
|
if not hglib:
|
||||||
|
log.error('hglib not present')
|
||||||
|
return False
|
||||||
|
return __virtualname__
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(__opts__):
|
||||||
|
"""Initialise
|
||||||
|
|
||||||
|
This is called every time a minion calls this external pillar.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def ext_pillar(minion_id, pillar, repo, branch='default', root=None):
|
||||||
|
'''
|
||||||
|
Extract pillar from an hg repository
|
||||||
|
'''
|
||||||
|
with Repo(repo) as repo:
|
||||||
|
repo.update(branch)
|
||||||
|
envname = 'base' if branch == 'default' else branch
|
||||||
|
if root:
|
||||||
|
path = os.path.normpath(os.path.join(repo.working_dir, root))
|
||||||
|
else:
|
||||||
|
path = repo.working_dir
|
||||||
|
|
||||||
|
# Do not recurse, Pillar will call ext_pillar again!
|
||||||
|
if __opts__['pillar_roots'].get(envname, []) == [path]:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
opts = copy.deepcopy(__opts__)
|
||||||
|
opts['pillar_roots'][envname] = [path]
|
||||||
|
pil = salt.pillar.Pillar(opts, __grains__, minion_id, envname)
|
||||||
|
return pil.compile_pillar()
|
||||||
|
|
||||||
|
|
||||||
|
def update(repo_uri):
|
||||||
|
"""Execute an hg pull on all the repos"""
|
||||||
|
with Repo(repo_uri) as repo:
|
||||||
|
repo.pull()
|
||||||
|
|
||||||
|
|
||||||
|
def envs():
|
||||||
|
"""Return a list of branches that can be used as environments"""
|
||||||
|
|
||||||
|
|
||||||
|
def purge_cache():
|
||||||
|
"""Purge the hg_pillar cache"""
|
||||||
|
|
||||||
|
|
||||||
|
class Repo(object):
|
||||||
|
'''
|
||||||
|
Deal with remote hg (mercurial) repository for Pillar
|
||||||
|
'''
|
||||||
|
|
||||||
|
def __init__(self, repo_uri):
|
||||||
|
''' Initialize a hg repo (or open it if it already exists) '''
|
||||||
|
self.repo_uri = repo_uri
|
||||||
|
cachedir = os.path.join(__opts__['cachedir'], 'hg_pillar')
|
||||||
|
hash_type = getattr(hashlib, __opts__.get('hash_type', 'md5'))
|
||||||
|
repo_hash = hash_type(repo_uri).hexdigest()
|
||||||
|
self.working_dir = os.path.join(cachedir, repo_hash)
|
||||||
|
if not os.path.isdir(self.working_dir):
|
||||||
|
self.repo = hglib.clone(repo_uri, self.working_dir)
|
||||||
|
self.repo.open()
|
||||||
|
else:
|
||||||
|
self.repo = hglib.open(self.working_dir)
|
||||||
|
|
||||||
|
def pull(self):
|
||||||
|
log.debug('Updating hg repo from hg_pillar module (pull)')
|
||||||
|
self.repo.pull()
|
||||||
|
|
||||||
|
def update(self, branch='default'):
|
||||||
|
''' ensure we are using the latest revision in the hg repository '''
|
||||||
|
log.debug('Updating hg repo from hg_pillar module (pull)')
|
||||||
|
self.repo.pull()
|
||||||
|
log.debug('Updating hg repo from hg_pillar module (update)')
|
||||||
|
self.repo.update(branch, clean=True)
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""Cleanup mercurial command server"""
|
||||||
|
self.repo.close()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
self.close()
|
72
tests/unit/pillar/hg_test.py
Normal file
72
tests/unit/pillar/hg_test.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
'''test for pillar hg_pillar.py'''
|
||||||
|
|
||||||
|
# Import python libs
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
# Import Salt Testing libs
|
||||||
|
from salttesting import TestCase, skipIf
|
||||||
|
from salttesting.mock import NO_MOCK, NO_MOCK_REASON
|
||||||
|
|
||||||
|
import integration
|
||||||
|
|
||||||
|
COMMIT_USER_NAME = 'test_user'
|
||||||
|
# file contents
|
||||||
|
PILLAR_CONTENT = {'gna': 'hello'}
|
||||||
|
FILE_DATA = {
|
||||||
|
'top.sls': {'base': {'*': ['user']}},
|
||||||
|
'user.sls': PILLAR_CONTENT
|
||||||
|
}
|
||||||
|
|
||||||
|
# Import Salt Libs
|
||||||
|
from salt.pillar import hg_pillar
|
||||||
|
HGLIB = hg_pillar.hglib
|
||||||
|
|
||||||
|
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||||
|
@skipIf(HGLIB == None, 'python-hglib no')
|
||||||
|
class HgPillarTestCase(TestCase, integration.AdaptedConfigurationTestCaseMixIn):
|
||||||
|
'test hg_pillar pillar'
|
||||||
|
maxDiff = None
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(HgPillarTestCase, self).setUp()
|
||||||
|
self.tmpdir = tempfile.mkdtemp(dir=integration.SYS_TMP_DIR)
|
||||||
|
cachedir = os.path.join(self.tmpdir, 'cachedir')
|
||||||
|
os.makedirs(os.path.join(cachedir, 'hg_pillar'))
|
||||||
|
self.hg_repo_path = self._create_hg_repo()
|
||||||
|
hg_pillar.__opts__ = {
|
||||||
|
'cachedir': cachedir,
|
||||||
|
'pillar_roots': {},
|
||||||
|
'file_roots': {},
|
||||||
|
'state_top': 'top.sls',
|
||||||
|
'extension_modules': '',
|
||||||
|
'renderer': 'yaml_jinja',
|
||||||
|
'pillar_opts': False
|
||||||
|
}
|
||||||
|
hg_pillar.__grains__ = {}
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
shutil.rmtree(self.tmpdir)
|
||||||
|
super(HgPillarTestCase, self).tearDown()
|
||||||
|
|
||||||
|
def _create_hg_repo(self):
|
||||||
|
'create repo in tempdir'
|
||||||
|
hg_repo = os.path.join(self.tmpdir, 'repo_pillar')
|
||||||
|
os.makedirs(hg_repo)
|
||||||
|
subprocess.check_call(["hg", "init", hg_repo])
|
||||||
|
for filename in FILE_DATA:
|
||||||
|
with open(os.path.join(hg_repo, filename), 'w') as data_file:
|
||||||
|
yaml.dump(FILE_DATA[filename], data_file)
|
||||||
|
subprocess.check_call(['hg', 'ci', '-A', '-R', hg_repo, '-m', 'first commit', '-u', COMMIT_USER_NAME])
|
||||||
|
return hg_repo
|
||||||
|
|
||||||
|
def test_base(self):
|
||||||
|
'check hg repo is imported correctly'
|
||||||
|
mypillar = hg_pillar.ext_pillar('*', None, 'file://{0}'.format(self.hg_repo_path))
|
||||||
|
self.assertEqual(PILLAR_CONTENT, mypillar)
|
Loading…
Reference in New Issue
Block a user