salt/pillar/hg_pillar.py
# -*- coding: utf-8 -*-
# Copyright (C) 2014 Floris Bruynooghe <flub@devork.be>
r'''
Use remote Mercurial repository as a Pillar source.
.. versionadded:: 2015.8.0
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 configured in the master config file as such:
.. code-block:: yaml
ext_pillar:
- hg: ssh://hg@example.co/user/repo
'''
# Import Python Libs
from __future__ import absolute_import, print_function, unicode_literals
import copy
import hashlib
import logging
import os
# Import Salt Libs
import salt.pillar
import salt.utils.stringutils
# Import Third Party Libs
from salt.ext import six
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
opts = copy.deepcopy(__opts__)
opts['pillar_roots'][envname] = [path]
pil = salt.pillar.Pillar(opts, __grains__, minion_id, envname)
return pil.compile_pillar(ext=False)
def update(repo_uri):
'''
Execute an hg pull on all the repos
'''
with Repo(repo_uri) as repo:
repo.pull()
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'))
if six.PY2:
repo_hash = hash_type(repo_uri).hexdigest()
else:
repo_hash = hash_type(salt.utils.stringutils.to_bytes(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()