Merge pull request #50784 from sathieu/dynamic_file_roots

Allow dynamic file_roots
This commit is contained in:
Daniel Wozniak 2019-02-08 11:09:39 -07:00 committed by GitHub
commit f7d50b13b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 61 additions and 13 deletions

View file

@ -2599,6 +2599,8 @@ can have multiple root directories. The subdirectories in the multiple file
roots cannot match, otherwise the downloaded files will not be able to be
reliably ensured. A base environment is required to house the top file.
As of 2018.3.5 and 2019.2.1, it is possible to have `__env__` as a catch-all environment.
Example:
.. code-block:: yaml
@ -2612,6 +2614,8 @@ Example:
prod:
- /srv/salt/prod/services
- /srv/salt/prod/states
__env__:
- /srv/salt/default
.. note::
For masterless Salt, this parameter must be specified in the minion config

View file

@ -734,6 +734,19 @@ Then the ``roots`` backend (the default backend of files in ``/srv/salt``) will
be searched first for the requested file; then, if it is not found on the
master, each configured git remote will be searched.
.. note::
This can be used together with `file_roots` accepting `__env__` as a catch-all
environment, since 2018.3.5 and 2019.2.1:
.. code-block:: yaml
file_roots:
base:
- /srv/salt
__env__:
- /srv/salt
Branches, Environments, and Top Files
=====================================

View file

@ -51,7 +51,11 @@ def find_file(path, saltenv='base', **kwargs):
if os.path.isabs(path):
return fnd
if saltenv not in __opts__['file_roots']:
return fnd
if '__env__' in __opts__['file_roots']:
log.debug("salt environment '%s' maps to __env__ file_roots directory", saltenv)
saltenv = '__env__'
else:
return fnd
def _add_file_stat(fnd):
'''
@ -220,6 +224,9 @@ def file_hash(load, fnd):
if 'path' not in load or 'saltenv' not in load:
return ''
path = fnd['path']
saltenv = load['saltenv']
if saltenv not in __opts__['file_roots'] and '__env__' in __opts__['file_roots']:
saltenv = '__env__'
ret = {}
# if the file doesn't exist, we can't get a hash
@ -234,7 +241,7 @@ def file_hash(load, fnd):
cache_path = os.path.join(__opts__['cachedir'],
'roots',
'hash',
load['saltenv'],
saltenv,
'{0}.hash.{1}'.format(fnd['rel'],
__opts__['hash_type']))
# if we have a cache, serve that if the mtime hasn't changed
@ -293,8 +300,13 @@ def _file_lists(load, form):
# "env" is not supported; Use "saltenv".
load.pop('env')
if load['saltenv'] not in __opts__['file_roots']:
return []
saltenv = load['saltenv']
if saltenv not in __opts__['file_roots']:
if '__env__' in __opts__['file_roots']:
log.debug("salt environment '%s' maps to __env__ file_roots directory", saltenv)
saltenv = '__env__'
else:
return []
list_cachedir = os.path.join(__opts__['cachedir'], 'file_lists', 'roots')
if not os.path.isdir(list_cachedir):
@ -303,8 +315,8 @@ def _file_lists(load, form):
except os.error:
log.critical('Unable to make cachedir %s', list_cachedir)
return []
list_cache = os.path.join(list_cachedir, '{0}.p'.format(salt.utils.files.safe_filename_leaf(load['saltenv'])))
w_lock = os.path.join(list_cachedir, '.{0}.w'.format(salt.utils.files.safe_filename_leaf(load['saltenv'])))
list_cache = os.path.join(list_cachedir, '{0}.p'.format(salt.utils.files.safe_filename_leaf(saltenv)))
w_lock = os.path.join(list_cachedir, '.{0}.w'.format(salt.utils.files.safe_filename_leaf(saltenv)))
cache_match, refresh_cache, save_cache = \
salt.fileserver.check_file_list_cache(
__opts__, form, list_cache, w_lock
@ -390,7 +402,7 @@ def _file_lists(load, form):
# (i.e. the "path" variable)
ret['links'][rel_path] = link_dest
for path in __opts__['file_roots'][load['saltenv']]:
for path in __opts__['file_roots'][saltenv]:
for root, dirs, files in salt.utils.path.os_walk(
path,
followlinks=__opts__['fileserver_followsymlinks']):
@ -445,7 +457,7 @@ def symlink_list(load):
load.pop('env')
ret = {}
if load['saltenv'] not in __opts__['file_roots']:
if load['saltenv'] not in __opts__['file_roots'] and '__env__' not in __opts__['file_roots']:
return ret
if 'prefix' in load:

View file

@ -3767,12 +3767,14 @@ class BaseHighState(object):
statefiles = []
for saltenv, states in six.iteritems(matches):
for sls_match in states:
try:
if saltenv in self.avail:
statefiles = fnmatch.filter(self.avail[saltenv], sls_match)
except KeyError:
all_errors.extend(
['No matching salt environment for environment '
'\'{0}\' found'.format(saltenv)]
elif '__env__' in self.avail:
statefiles = fnmatch.filter(self.avail['__env__'], sls_match)
else:
all_errors.append(
'No matching salt environment for environment '
'\'{0}\' found'.format(saltenv)
)
# if we did not found any sls in the fileserver listing, this
# may be because the sls was generated or added later, we can

View file

@ -179,3 +179,20 @@ class RootsTest(TestCase, AdaptedConfigurationTestCaseMixin, LoaderModuleMockMix
finally:
if self.test_symlink_list_file_roots:
self.opts['file_roots'] = orig_file_roots
def test_dynamic_file_roots(self):
dyn_root_dir = tempfile.mkdtemp(dir=TMP)
top_sls = os.path.join(dyn_root_dir, 'top.sls')
with salt.utils.files.fopen(top_sls, 'w') as fp_:
fp_.write("{{saltenv}}:\n '*':\n - dynamo\n")
dynamo_sls = os.path.join(dyn_root_dir, 'dynamo.sls')
with salt.utils.files.fopen(dynamo_sls, 'w') as fp_:
fp_.write("foo:\n test.nop\n")
opts = {'file_roots': copy.copy(self.opts['file_roots'])}
opts['file_roots']['__env__'] = [dyn_root_dir]
with patch.dict(roots.__opts__, opts):
ret1 = roots.find_file('dynamo.sls', 'dyn')
ret2 = roots.file_list({'saltenv': 'dyn'})
self.assertEqual('dynamo.sls', ret1['rel'])
self.assertIn('top.sls', ret2)
self.assertIn('dynamo.sls', ret2)