mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch '2015.5' into '2015.8'
Conflicts: - salt/modules/saltutil.py - salt/states/archive.py
This commit is contained in:
commit
b8e4706074
4 changed files with 257 additions and 70 deletions
|
@ -258,18 +258,22 @@ def update(version=None):
|
|||
|
||||
def sync_beacons(saltenv=None, refresh=True):
|
||||
'''
|
||||
Sync the beacons from the _beacons directory on the salt master file
|
||||
server. This function is environment aware, pass the desired environment
|
||||
to grab the contents of the _beacons directory, base is the default
|
||||
environment.
|
||||
|
||||
.. versionadded:: 2015.5.1
|
||||
|
||||
Sync the beacons from the ``salt://_beacons`` directory on the Salt
|
||||
fileserver. This function is environment-aware, pass the desired
|
||||
environment to grab the contents of the ``_beacons`` directory from that
|
||||
environment. The default environment, if none is specified, is ``base``.
|
||||
|
||||
refresh : True
|
||||
Also refresh the beacons available to the minion.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_beacons
|
||||
salt '*' saltutil.sync_beacons saltenv=dev
|
||||
'''
|
||||
ret = _sync('beacons', saltenv)
|
||||
if refresh:
|
||||
|
@ -279,18 +283,23 @@ def sync_beacons(saltenv=None, refresh=True):
|
|||
|
||||
def sync_sdb(saltenv=None, refresh=False):
|
||||
'''
|
||||
Sync sdb modules from the _sdb directory on the salt master file
|
||||
server. This function is environment aware, pass the desired environment
|
||||
to grab the contents of the _sdb directory, base is the default
|
||||
environment.
|
||||
|
||||
.. versionadded:: 2015.5.7
|
||||
|
||||
Sync sdb modules from the ``salt://_sdb`` directory on the Salt
|
||||
fileserver. This function is environment-aware, pass the desired
|
||||
environment to grab the contents of the ``_sdb`` directory from that
|
||||
environment. The default environment, if none is specified, is ``base``.
|
||||
|
||||
refresh : False
|
||||
This argument has no affect and is included for consistency with the
|
||||
other sync functions.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_sdb
|
||||
salt '*' saltutil.sync_sdb saltenv=dev
|
||||
'''
|
||||
ret = _sync('sdb', saltenv)
|
||||
return ret
|
||||
|
@ -298,10 +307,13 @@ def sync_sdb(saltenv=None, refresh=False):
|
|||
|
||||
def sync_modules(saltenv=None, refresh=True):
|
||||
'''
|
||||
Sync the modules from the _modules directory on the salt master file
|
||||
server. This function is environment aware, pass the desired environment
|
||||
to grab the contents of the _modules directory, base is the default
|
||||
environment.
|
||||
Sync the modules from the ``salt://_modules`` directory on the Salt
|
||||
fileserver. This function is environment-aware, pass the desired
|
||||
environment to grab the contents of the ``_modules`` directory from that
|
||||
environment. The default environment, if none is specified, is ``base``.
|
||||
|
||||
refresh : True
|
||||
Also refresh the execution modules available to the minion.
|
||||
|
||||
.. important::
|
||||
|
||||
|
@ -320,13 +332,12 @@ def sync_modules(saltenv=None, refresh=True):
|
|||
See :ref:`here <reloading-modules>` for a more detailed explanation of
|
||||
why this is necessary.
|
||||
|
||||
.. versionadded:: 2015.5.1
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_modules
|
||||
salt '*' saltutil.sync_modules saltenv=dev
|
||||
'''
|
||||
ret = _sync('modules', saltenv)
|
||||
if refresh:
|
||||
|
@ -336,16 +347,20 @@ def sync_modules(saltenv=None, refresh=True):
|
|||
|
||||
def sync_states(saltenv=None, refresh=True):
|
||||
'''
|
||||
Sync the states from the _states directory on the salt master file
|
||||
server. This function is environment aware, pass the desired environment
|
||||
to grab the contents of the _states directory, base is the default
|
||||
environment.
|
||||
Sync the states from the ``salt://_states`` directory on the Salt
|
||||
fileserver. This function is environment-aware, pass the desired
|
||||
environment to grab the contents of the ``_states`` directory from that
|
||||
environment. The default environment, if none is specified, is ``base``.
|
||||
|
||||
refresh : True
|
||||
Also refresh the execution modules available to the minion.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_states
|
||||
salt '*' saltutil.sync_states saltenv=dev
|
||||
'''
|
||||
ret = _sync('states', saltenv)
|
||||
if refresh:
|
||||
|
@ -355,16 +370,21 @@ def sync_states(saltenv=None, refresh=True):
|
|||
|
||||
def sync_grains(saltenv=None, refresh=True):
|
||||
'''
|
||||
Sync the grains from the _grains directory on the salt master file
|
||||
server. This function is environment aware, pass the desired environment
|
||||
to grab the contents of the _grains directory, base is the default
|
||||
environment.
|
||||
Sync the grains from the ``salt://_grains`` directory on the Salt
|
||||
fileserver. This function is environment-aware, pass the desired
|
||||
environment to grab the contents of the ``_grains`` directory from that
|
||||
environment. The default environment, if none is specified, is ``base``.
|
||||
|
||||
refresh : True
|
||||
Also refresh the execution modules available to the minion, and refresh
|
||||
pillar data.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_grains
|
||||
salt '*' saltutil.sync_grains saltenv=dev
|
||||
'''
|
||||
ret = _sync('grains', saltenv)
|
||||
if refresh:
|
||||
|
@ -375,16 +395,20 @@ def sync_grains(saltenv=None, refresh=True):
|
|||
|
||||
def sync_renderers(saltenv=None, refresh=True):
|
||||
'''
|
||||
Sync the renderers from the _renderers directory on the salt master file
|
||||
server. This function is environment aware, pass the desired environment
|
||||
to grab the contents of the _renderers directory, base is the default
|
||||
environment.
|
||||
Sync the renderers from the ``salt://_renderers`` directory on the Salt
|
||||
fileserver. This function is environment-aware, pass the desired
|
||||
environment to grab the contents of the ``_renderers`` directory from that
|
||||
environment. The default environment, if none is specified, is ``base``.
|
||||
|
||||
refresh : True
|
||||
Also refresh the execution modules available to the minion.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_renderers
|
||||
salt '*' saltutil.sync_renderers saltenv=dev
|
||||
'''
|
||||
ret = _sync('renderers', saltenv)
|
||||
if refresh:
|
||||
|
@ -394,16 +418,20 @@ def sync_renderers(saltenv=None, refresh=True):
|
|||
|
||||
def sync_returners(saltenv=None, refresh=True):
|
||||
'''
|
||||
Sync the returners from the _returners directory on the salt master file
|
||||
server. This function is environment aware, pass the desired environment
|
||||
to grab the contents of the _returners directory, base is the default
|
||||
environment.
|
||||
Sync the returners from the ``salt://_returners`` directory on the Salt
|
||||
fileserver. This function is environment-aware, pass the desired
|
||||
environment to grab the contents of the ``_returners`` directory from that
|
||||
environment. The default environment, if none is specified, is ``base``.
|
||||
|
||||
refresh : True
|
||||
Also refresh the execution modules available to the minion.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_returners
|
||||
salt '*' saltutil.sync_returners saltenv=dev
|
||||
'''
|
||||
ret = _sync('returners', saltenv)
|
||||
if refresh:
|
||||
|
@ -413,16 +441,19 @@ def sync_returners(saltenv=None, refresh=True):
|
|||
|
||||
def sync_proxymodules(saltenv=None, refresh=False):
|
||||
'''
|
||||
Sync the proxy modules from the _proxy directory on the salt master file
|
||||
server. This function is environment aware, pass the desired environment
|
||||
to grab the contents of the _returners directory, base is the default
|
||||
environment.
|
||||
.. versionadded:: 2015.8.2
|
||||
|
||||
Sync the proxy modules from the ``salt://_proxy`` directory on the Salt
|
||||
fileserver. This function is environment-aware, pass the desired
|
||||
environment to grab the contents of the ``_proxy`` directory from that
|
||||
environment. The default environment, if none is specified, is ``base``.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_proxymodules
|
||||
salt '*' saltutil.sync_proxymodules saltenv=dev
|
||||
'''
|
||||
ret = _sync('proxy', saltenv)
|
||||
if refresh:
|
||||
|
@ -432,16 +463,20 @@ def sync_proxymodules(saltenv=None, refresh=False):
|
|||
|
||||
def sync_output(saltenv=None, refresh=True):
|
||||
'''
|
||||
Sync the output modules from the _output directory on the salt master file
|
||||
server. This function is environment aware. Pass the desired environment
|
||||
to grab the contents of the _output directory. Base is the default
|
||||
environment.
|
||||
Sync the output modules from the ``salt://_output`` directory on the Salt
|
||||
fileserver. This function is environment-aware, pass the desired
|
||||
environment to grab the contents of the ``_output`` directory from that
|
||||
environment. The default environment, if none is specified, is ``base``.
|
||||
|
||||
refresh : True
|
||||
Also refresh the execution modules available to the minion.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_output
|
||||
salt '*' saltutil.sync_output saltenv=dev
|
||||
'''
|
||||
ret = _sync('output', saltenv)
|
||||
if refresh:
|
||||
|
@ -453,16 +488,22 @@ sync_outputters = salt.utils.alias_function(sync_output, 'sync_outputters')
|
|||
|
||||
def sync_utils(saltenv=None, refresh=True):
|
||||
'''
|
||||
Sync utility source files from the _utils directory on the salt master file
|
||||
server. This function is environment aware, pass the desired environment
|
||||
to grab the contents of the _utils directory, base is the default
|
||||
environment.
|
||||
.. versionadded:: 2014.7.0
|
||||
|
||||
Sync utility source files from the ``salt://_utils`` directory on the Salt
|
||||
fileserver. This function is environment-aware, pass the desired
|
||||
environment to grab the contents of the ``_utils`` directory from that
|
||||
environment. The default environment, if none is specified, is ``base``.
|
||||
|
||||
refresh : True
|
||||
Also refresh the execution modules available to the minion.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_utils
|
||||
salt '*' saltutil.sync_utils saltenv=dev
|
||||
'''
|
||||
ret = _sync('utils', saltenv)
|
||||
if refresh:
|
||||
|
@ -474,25 +515,65 @@ def sync_log_handlers(saltenv=None, refresh=True):
|
|||
'''
|
||||
.. versionadded:: 2015.8.0
|
||||
|
||||
Sync utility source files from the _log_handlers directory on the salt master file
|
||||
server. This function is environment aware, pass the desired environment
|
||||
to grab the contents of the _log_handlers directory, base is the default
|
||||
environment.
|
||||
Sync utility source files from the ``salt://_log_handlers`` directory on
|
||||
the Salt fileserver. This function is environment-aware, pass the desired
|
||||
environment to grab the contents of the ``_log_handlers`` directory from
|
||||
that environment. The default environment, if none is specified, is
|
||||
``base``.
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_log_handlers
|
||||
salt '*' saltutil.sync_log_handlers saltenv=dev
|
||||
'''
|
||||
ret = _sync('log_handlers', saltenv)
|
||||
if refresh:
|
||||
refresh_modules()
|
||||
|
||||
|
||||
def sync_pillar(saltenv=None, refresh=True):
|
||||
'''
|
||||
.. versionadded:: 2015.8.11,2016.3.2
|
||||
|
||||
Sync pillar modules from the ``salt://_pillar`` directory on the Salt
|
||||
fileserver. This function is environment-aware, pass the desired
|
||||
environment to grab the contents of the ``_pillar`` directory from that
|
||||
environment. The default environment, if none is specified, is ``base``.
|
||||
|
||||
refresh : True
|
||||
Also refresh the execution modules available to the minion, and refresh
|
||||
pillar data.
|
||||
|
||||
.. note::
|
||||
This function will raise an error if executed on a traditional (i.e.
|
||||
not masterless) minion
|
||||
|
||||
CLI Examples:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_pillar
|
||||
salt '*' saltutil.sync_pillar saltenv=dev
|
||||
'''
|
||||
if __opts__['file_client'] != 'local':
|
||||
raise CommandExecutionError(
|
||||
'Pillar modules can only be synced to masterless minions'
|
||||
)
|
||||
ret = _sync('pillar', saltenv)
|
||||
if refresh:
|
||||
refresh_modules()
|
||||
refresh_pillar()
|
||||
return ret
|
||||
|
||||
|
||||
def sync_all(saltenv=None, refresh=True):
|
||||
'''
|
||||
.. versionchanged:: 2015.8.11,2016.3.2
|
||||
On masterless minions, pillar modules are now synced, and refreshed
|
||||
when ``refresh`` is set to ``True``.
|
||||
|
||||
Sync down all of the dynamic modules from the file server for a specific
|
||||
environment. This function synchronizes custom modules, states, beacons,
|
||||
grains, returners, output modules, renderers, and utils.
|
||||
|
@ -522,6 +603,7 @@ def sync_all(saltenv=None, refresh=True):
|
|||
.. code-block:: bash
|
||||
|
||||
salt '*' saltutil.sync_all
|
||||
salt '*' saltutil.sync_all saltenv=dev
|
||||
'''
|
||||
log.debug('Syncing all')
|
||||
ret = {}
|
||||
|
@ -536,8 +618,12 @@ def sync_all(saltenv=None, refresh=True):
|
|||
ret['utils'] = sync_utils(saltenv, False)
|
||||
ret['log_handlers'] = sync_log_handlers(saltenv, False)
|
||||
ret['proxymodules'] = sync_proxymodules(saltenv, False)
|
||||
if __opts__['file_client'] == 'local':
|
||||
ret['pillar'] = sync_pillar(saltenv, False)
|
||||
if refresh:
|
||||
refresh_modules()
|
||||
if __opts__['file_client'] == 'local':
|
||||
refresh_pillar()
|
||||
return ret
|
||||
|
||||
|
||||
|
|
|
@ -204,7 +204,7 @@ def get_load(jid):
|
|||
{'jid': jid})
|
||||
data = cur.fetchone()
|
||||
if data:
|
||||
return json.loads(data)
|
||||
return json.loads(data[0].encode())
|
||||
_close_conn(conn)
|
||||
return {}
|
||||
|
||||
|
|
|
@ -15,8 +15,10 @@ from contextlib import closing
|
|||
|
||||
# Import 3rd-party libs
|
||||
import salt.ext.six as six
|
||||
from salt.ext.six.moves import shlex_quote as _cmd_quote
|
||||
|
||||
# Import salt libs
|
||||
import salt.utils
|
||||
from salt.exceptions import CommandExecutionError
|
||||
# remove after archive_user deprecation.
|
||||
from salt.utils import warn_until
|
||||
|
@ -35,6 +37,21 @@ def __virtual__():
|
|||
else False
|
||||
|
||||
|
||||
def _is_bsdtar():
|
||||
return 'bsdtar' in __salt__['cmd.run'](['tar', '--version'],
|
||||
python_shell=False)
|
||||
|
||||
|
||||
def _cleanup_destdir(name):
|
||||
'''
|
||||
Attempt to remove the specified directory
|
||||
'''
|
||||
try:
|
||||
os.rmdir(name)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def extracted(name,
|
||||
source,
|
||||
archive_format,
|
||||
|
@ -87,7 +104,7 @@ def extracted(name,
|
|||
- if_missing: /opt/graylog2-server-0.9.6p1/
|
||||
|
||||
name
|
||||
Directory name where to extract the archive
|
||||
Location where archive should be extracted
|
||||
|
||||
source
|
||||
Archive source, same syntax as file.managed source argument.
|
||||
|
@ -116,21 +133,37 @@ def extracted(name,
|
|||
.. versionadded:: 2015.8.0
|
||||
|
||||
if_missing
|
||||
Some archives, such as tar, extract themselves in a subfolder.
|
||||
This directive can be used to validate if the archive had been
|
||||
previously extracted.
|
||||
If specified, this path will be checked, and if it exists then the
|
||||
archive will not be extracted. This can be helpful if the archive
|
||||
extracts all files into a subfolder. This path can be either a
|
||||
directory or a file, so this option can also be used to check for a
|
||||
semaphore file and conditionally skip extraction.
|
||||
|
||||
tar_options
|
||||
Required if used with ``archive_format: tar``, otherwise optional.
|
||||
It needs to be the tar argument specific to the archive being extracted,
|
||||
such as 'J' for LZMA or 'v' to verbosely list files processed.
|
||||
Using this option means that the tar executable on the target will
|
||||
be used, which is less platform independent.
|
||||
Main operators like -x, --extract, --get, -c and -f/--file
|
||||
**should not be used** here.
|
||||
If ``archive_format`` is ``zip`` or ``rar`` and this option is not set,
|
||||
then the Python tarfile module is used. The tarfile module supports gzip
|
||||
and bz2 in Python 2.
|
||||
If ``archive_format`` is set to ``tar``, this option can be used to
|
||||
specify a string of additional arguments to pass to the tar command. If
|
||||
``archive_format`` is set to ``tar`` and this option is *not* used,
|
||||
then the minion will attempt to use Python's native tarfile_ support to
|
||||
extract it. Python's native tarfile_ support can only handle gzip and
|
||||
bzip2 compression, however.
|
||||
|
||||
.. versionchanged:: 2015.8.11,2016.3.2
|
||||
XZ-compressed archives no longer require ``J`` to manually be set
|
||||
in the ``tar_options``, they are now detected automatically and
|
||||
Salt will extract them using ``xz-utils``. This is a more
|
||||
platform-independent solution, as not all tar implementations
|
||||
support the ``J`` argument for extracting archives.
|
||||
|
||||
.. note::
|
||||
Main operators like -x, --extract, --get, -c and -f/--file **should
|
||||
not be used** here.
|
||||
|
||||
Using this option means that the ``tar`` command will be used,
|
||||
which is less platform-independent, so keep this in mind when using
|
||||
this option; the options must be valid options for the ``tar``
|
||||
implementation on the minion's OS.
|
||||
|
||||
.. _tarfile: https://docs.python.org/2/library/tarfile.html
|
||||
|
||||
keep
|
||||
Keep the archive in the minion's cache
|
||||
|
@ -220,7 +253,7 @@ def extracted(name,
|
|||
log.debug('failed to download {0}'.format(source))
|
||||
return file_result
|
||||
else:
|
||||
log.debug('Archive %s is already in cache', name)
|
||||
log.debug('Archive %s is already in cache', source)
|
||||
|
||||
if __opts__['test']:
|
||||
ret['result'] = None
|
||||
|
@ -232,7 +265,15 @@ def extracted(name,
|
|||
)
|
||||
return ret
|
||||
|
||||
__salt__['file.makedirs'](name, user=user, group=group)
|
||||
created_destdir = False
|
||||
if __salt__['file.file_exists'](name.rstrip('/')):
|
||||
ret['result'] = False
|
||||
ret['comment'] = ('{0} exists and is not a directory'
|
||||
.format(name.rstrip('/')))
|
||||
return ret
|
||||
elif not __salt__['file.directory_exists'](name):
|
||||
__salt__['file.makedirs'](name, user=archive_user)
|
||||
created_destdir = True
|
||||
|
||||
log.debug('Extracting {0} to {1}'.format(filename, name))
|
||||
if archive_format == 'zip':
|
||||
|
@ -241,11 +282,70 @@ def extracted(name,
|
|||
files = __salt__['archive.unrar'](filename, name)
|
||||
else:
|
||||
if tar_options is None:
|
||||
with closing(tarfile.open(filename, 'r')) as tar:
|
||||
files = tar.getnames()
|
||||
tar.extractall(name)
|
||||
try:
|
||||
with closing(tarfile.open(filename, 'r')) as tar:
|
||||
files = tar.getnames()
|
||||
tar.extractall(name)
|
||||
except tarfile.ReadError:
|
||||
if salt.utils.which('xz'):
|
||||
if __salt__['cmd.retcode'](['xz', '-l', filename],
|
||||
python_shell=False,
|
||||
ignore_retcode=True) == 0:
|
||||
# XZ-compressed data
|
||||
log.debug(
|
||||
'Tar file is XZ-compressed, attempting '
|
||||
'decompression and extraction using xz-utils '
|
||||
'and the tar command'
|
||||
)
|
||||
# Must use python_shell=True here because not all tar
|
||||
# implementations support the -J flag for decompressing
|
||||
# XZ-compressed data. We need to dump the decompressed
|
||||
# data to stdout and pipe it to tar for extraction.
|
||||
cmd = 'xz --decompress --stdout {0} | tar xvf -'
|
||||
results = __salt__['cmd.run_all'](
|
||||
cmd.format(_cmd_quote(filename)),
|
||||
cwd=name,
|
||||
python_shell=True)
|
||||
if results['retcode'] != 0:
|
||||
if created_destdir:
|
||||
_cleanup_destdir(name)
|
||||
ret['result'] = False
|
||||
ret['changes'] = results
|
||||
return ret
|
||||
if _is_bsdtar():
|
||||
files = results['stderr']
|
||||
else:
|
||||
files = results['stdout']
|
||||
else:
|
||||
# Failed to open tar archive and it is not
|
||||
# XZ-compressed, gracefully fail the state
|
||||
if created_destdir:
|
||||
_cleanup_destdir(name)
|
||||
ret['result'] = False
|
||||
ret['comment'] = (
|
||||
'Failed to read from tar archive using Python\'s '
|
||||
'native tar file support. If archive is '
|
||||
'compressed using something other than gzip or '
|
||||
'bzip2, the \'tar_options\' parameter may be '
|
||||
'required to pass the correct options to the tar '
|
||||
'command in order to extract the archive.'
|
||||
)
|
||||
return ret
|
||||
else:
|
||||
if created_destdir:
|
||||
_cleanup_destdir(name)
|
||||
ret['result'] = False
|
||||
ret['comment'] = (
|
||||
'Failed to read from tar archive. If it is '
|
||||
'XZ-compressed, install xz-utils to attempt '
|
||||
'extraction.'
|
||||
)
|
||||
return ret
|
||||
else:
|
||||
tar_opts = tar_options.split(' ')
|
||||
try:
|
||||
tar_opts = tar_options.split(' ')
|
||||
except AttributeError:
|
||||
tar_opts = str(tar_options).split(' ')
|
||||
|
||||
tar_cmd = ['tar']
|
||||
tar_shortopts = 'x'
|
||||
|
@ -271,7 +371,7 @@ def extracted(name,
|
|||
ret['result'] = False
|
||||
ret['changes'] = results
|
||||
return ret
|
||||
if 'bsdtar' in __salt__['cmd.run']('tar --version', python_shell=False):
|
||||
if _is_bsdtar():
|
||||
files = results['stderr']
|
||||
else:
|
||||
files = results['stdout']
|
||||
|
|
|
@ -67,6 +67,7 @@ class SysModuleTest(integration.ModuleCase):
|
|||
'runtests_decorators.missing_depends',
|
||||
'runtests_decorators.missing_depends_will_fallback',
|
||||
'swift.head',
|
||||
'glance.warn_until',
|
||||
'yumpkg.expand_repo_def',
|
||||
'yumpkg5.expand_repo_def',
|
||||
'container_resource.run',
|
||||
|
|
Loading…
Add table
Reference in a new issue