mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge branch 'master' into remove_payload_Serial
This commit is contained in:
commit
94763bb50b
18 changed files with 261 additions and 116 deletions
1
changelog/45450.added.md
Normal file
1
changelog/45450.added.md
Normal file
|
@ -0,0 +1 @@
|
|||
Added syncing of custom salt-ssh wrappers
|
1
changelog/64450.fixed.md
Normal file
1
changelog/64450.fixed.md
Normal file
|
@ -0,0 +1 @@
|
|||
Fixed issue uninstalling duplicate packages in ``win_appx`` execution module
|
1
changelog/64460.removed.md
Normal file
1
changelog/64460.removed.md
Normal file
|
@ -0,0 +1 @@
|
|||
Remove netmiko_conn and pyeapi_conn from salt.modules.napalm_mod
|
1
changelog/64462.changed.md
Normal file
1
changelog/64462.changed.md
Normal file
|
@ -0,0 +1 @@
|
|||
changed 'gpg_decrypt_must_succeed' default from False to True
|
|
@ -10,12 +10,12 @@ exist on the subject, to either execute in an imperative fashion where things
|
|||
are executed in the order in which they are defined, or in a declarative
|
||||
fashion where dependencies need to be mapped between objects.
|
||||
|
||||
Imperative ordering is finite and generally considered easier to write, but
|
||||
Imperative ordering is deterministic and generally considered easier to write, but
|
||||
declarative ordering is much more powerful and flexible but generally considered
|
||||
more difficult to create.
|
||||
|
||||
Salt has been created to get the best of both worlds. States are evaluated in
|
||||
a finite order, which guarantees that states are always executed in the same
|
||||
a deterministic order, which guarantees that states are always executed in the same
|
||||
order, and the states runtime is declarative, making Salt fully aware of
|
||||
dependencies via the `requisite` system.
|
||||
|
||||
|
@ -26,7 +26,7 @@ State Auto Ordering
|
|||
|
||||
.. versionadded: 0.17.0
|
||||
|
||||
Salt always executes states in a finite manner, meaning that they will always
|
||||
Salt always executes states in a deterministic manner, meaning that they will always
|
||||
execute in the same order regardless of the system that is executing them. This
|
||||
evaluation order makes it easy to know what order the states will be executed in,
|
||||
but it is important to note that the requisite system will override the ordering
|
||||
|
|
|
@ -144,7 +144,7 @@ SDB ``salt.sdb`` (:ref:`index <all-salt.sdb>`) ``
|
|||
Serializer ``salt.serializers`` (:ref:`index <all-salt.serializers>`) ``serializers`` [#no-fs]_ ``serializers_dirs``
|
||||
SPM pkgdb ``salt.spm.pkgdb`` ``pkgdb`` [#no-fs]_ ``pkgdb_dirs``
|
||||
SPM pkgfiles ``salt.spm.pkgfiles`` ``pkgfiles`` [#no-fs]_ ``pkgfiles_dirs``
|
||||
SSH Wrapper ``salt.client.ssh.wrapper`` ``wrapper`` [#no-fs]_ ``wrapper_dirs``
|
||||
SSH Wrapper ``salt.client.ssh.wrapper`` ``wrapper`` ``wrapper_dirs``
|
||||
State ``salt.states`` (:ref:`index <all-salt.states>`) ``states`` ``states_dirs``
|
||||
Thorium ``salt.thorium`` (:ref:`index <all-salt.thorium>`) ``thorium`` ``thorium_dirs``
|
||||
Tokens ``salt.tokens`` ``tokens`` ``tokens_dirs``
|
||||
|
|
|
@ -10,8 +10,8 @@ Many of the most powerful and useful engineering solutions are founded on
|
|||
simple principles. Salt States strive to do just that: K.I.S.S. (Keep It
|
||||
Stupidly Simple)
|
||||
|
||||
The core of the Salt State system is the SLS, or **S**\ a\ **L**\ t
|
||||
**S**\ tate file. The SLS is a representation of the state in which
|
||||
The core of the Salt State system is the SLS, or **S**\ tructured **L**\ ayered **S**\ tate.
|
||||
The SLS is a representation of the state in which
|
||||
a system should be in, and is set up to contain this data in a simple format.
|
||||
This is often called configuration management.
|
||||
|
||||
|
|
|
@ -1089,7 +1089,7 @@ DEFAULT_MINION_OPTS = immutabletypes.freeze(
|
|||
"decrypt_pillar_delimiter": ":",
|
||||
"decrypt_pillar_default": "gpg",
|
||||
"decrypt_pillar_renderers": ["gpg"],
|
||||
"gpg_decrypt_must_succeed": False,
|
||||
"gpg_decrypt_must_succeed": True,
|
||||
# Update intervals
|
||||
"roots_update_interval": DEFAULT_INTERVAL,
|
||||
"gitfs_update_interval": DEFAULT_INTERVAL,
|
||||
|
@ -1329,7 +1329,7 @@ DEFAULT_MASTER_OPTS = immutabletypes.freeze(
|
|||
"decrypt_pillar_delimiter": ":",
|
||||
"decrypt_pillar_default": "gpg",
|
||||
"decrypt_pillar_renderers": ["gpg"],
|
||||
"gpg_decrypt_must_succeed": False,
|
||||
"gpg_decrypt_must_succeed": True,
|
||||
"thoriumenv": None,
|
||||
"thorium_top": "top.sls",
|
||||
"thorium_interval": 0.5,
|
||||
|
@ -2013,7 +2013,7 @@ def _read_conf_file(path):
|
|||
try:
|
||||
conf_opts = salt.utils.yaml.safe_load(conf_file) or {}
|
||||
except salt.utils.yaml.YAMLError as err:
|
||||
message = "Error parsing configuration file: {} - {}".format(path, err)
|
||||
message = f"Error parsing configuration file: {path} - {err}"
|
||||
log.error(message)
|
||||
if path.endswith("_schedule.conf"):
|
||||
# Create empty dictionary of config options
|
||||
|
@ -2110,7 +2110,7 @@ def load_config(path, env_var, default_path=None, exit_on_config_errors=True):
|
|||
# If the configuration file is missing, attempt to copy the template,
|
||||
# after removing the first header line.
|
||||
if not os.path.isfile(path):
|
||||
template = "{}.template".format(path)
|
||||
template = f"{path}.template"
|
||||
if os.path.isfile(template):
|
||||
log.debug("Writing %s based on %s", path, template)
|
||||
with salt.utils.files.fopen(path, "w") as out:
|
||||
|
@ -2780,7 +2780,7 @@ def apply_cloud_config(overrides, defaults=None):
|
|||
if alias not in config["providers"]:
|
||||
config["providers"][alias] = {}
|
||||
|
||||
detail["provider"] = "{}:{}".format(alias, driver)
|
||||
detail["provider"] = f"{alias}:{driver}"
|
||||
config["providers"][alias][driver] = detail
|
||||
elif isinstance(details, dict):
|
||||
if "driver" not in details:
|
||||
|
@ -2797,7 +2797,7 @@ def apply_cloud_config(overrides, defaults=None):
|
|||
if alias not in config["providers"]:
|
||||
config["providers"][alias] = {}
|
||||
|
||||
details["provider"] = "{}:{}".format(alias, driver)
|
||||
details["provider"] = f"{alias}:{driver}"
|
||||
config["providers"][alias][driver] = details
|
||||
|
||||
# Migrate old configuration
|
||||
|
@ -3068,7 +3068,7 @@ def apply_cloud_providers_config(overrides, defaults=None):
|
|||
for entry in val:
|
||||
|
||||
if "driver" not in entry:
|
||||
entry["driver"] = "-only-extendable-{}".format(ext_count)
|
||||
entry["driver"] = f"-only-extendable-{ext_count}"
|
||||
ext_count += 1
|
||||
|
||||
if key not in providers:
|
||||
|
@ -3111,7 +3111,7 @@ def apply_cloud_providers_config(overrides, defaults=None):
|
|||
details["driver"], provider_alias, alias, provider
|
||||
)
|
||||
)
|
||||
details["extends"] = "{}:{}".format(alias, provider)
|
||||
details["extends"] = f"{alias}:{provider}"
|
||||
# change provider details '-only-extendable-' to extended
|
||||
# provider name
|
||||
details["driver"] = provider
|
||||
|
@ -3132,10 +3132,10 @@ def apply_cloud_providers_config(overrides, defaults=None):
|
|||
)
|
||||
else:
|
||||
if driver in providers.get(extends):
|
||||
details["extends"] = "{}:{}".format(extends, driver)
|
||||
details["extends"] = f"{extends}:{driver}"
|
||||
elif "-only-extendable-" in providers.get(extends):
|
||||
details["extends"] = "{}:{}".format(
|
||||
extends, "-only-extendable-{}".format(ext_count)
|
||||
extends, f"-only-extendable-{ext_count}"
|
||||
)
|
||||
else:
|
||||
# We're still not aware of what we're trying to extend
|
||||
|
@ -3849,7 +3849,7 @@ def _update_discovery_config(opts):
|
|||
for key in opts["discovery"]:
|
||||
if key not in discovery_config:
|
||||
raise salt.exceptions.SaltConfigurationError(
|
||||
"Unknown discovery option: {}".format(key)
|
||||
f"Unknown discovery option: {key}"
|
||||
)
|
||||
if opts.get("__role") != "minion":
|
||||
for key in ["attempts", "pause", "match"]:
|
||||
|
|
|
@ -742,36 +742,6 @@ def netmiko_config(*config_commands, **kwargs):
|
|||
return __salt__["netmiko.send_config"](config_commands=config_commands, **kwargs)
|
||||
|
||||
|
||||
@proxy_napalm_wrap
|
||||
def netmiko_conn(**kwargs):
|
||||
"""
|
||||
.. versionadded:: 2019.2.0
|
||||
|
||||
Return the connection object with the network device, over Netmiko, passing
|
||||
the authentication details from the existing NAPALM connection.
|
||||
|
||||
.. warning::
|
||||
|
||||
This function is not suitable for CLI usage, more rather to be used
|
||||
in various Salt modules.
|
||||
|
||||
USAGE Example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
conn = __salt__['napalm.netmiko_conn']()
|
||||
res = conn.send_command('show interfaces')
|
||||
conn.disconnect()
|
||||
"""
|
||||
salt.utils.versions.warn_until(
|
||||
3007,
|
||||
"This 'napalm_mod.netmiko_conn' function as been deprecated and "
|
||||
"will be removed in the {version} release, as such, it has been "
|
||||
"made an internal function since it is not suitable for CLI usage",
|
||||
)
|
||||
return _netmiko_conn(**kwargs)
|
||||
|
||||
|
||||
@proxy_napalm_wrap
|
||||
def junos_rpc(cmd=None, dest=None, format=None, **kwargs):
|
||||
"""
|
||||
|
@ -1139,36 +1109,6 @@ def pyeapi_call(method, *args, **kwargs):
|
|||
return __salt__["pyeapi.call"](method, *args, **pyeapi_kwargs)
|
||||
|
||||
|
||||
@proxy_napalm_wrap
|
||||
def pyeapi_conn(**kwargs):
|
||||
"""
|
||||
.. versionadded:: 2019.2.0
|
||||
|
||||
Return the connection object with the Arista switch, over ``pyeapi``,
|
||||
passing the authentication details from the existing NAPALM connection.
|
||||
|
||||
.. warning::
|
||||
This function is not suitable for CLI usage, more rather to be used in
|
||||
various Salt modules, to reusing the established connection, as in
|
||||
opposite to opening a new connection for each task.
|
||||
|
||||
Usage example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
conn = __salt__['napalm.pyeapi_conn']()
|
||||
res1 = conn.run_commands('show version')
|
||||
res2 = conn.get_config(as_string=True)
|
||||
"""
|
||||
salt.utils.versions.warn_until(
|
||||
3007,
|
||||
"This 'napalm_mod.pyeapi_conn' function as been deprecated and "
|
||||
"will be removed in the {version} release, as such, it has been "
|
||||
"made an internal function since it is not suitable for CLI usage",
|
||||
)
|
||||
return _pyeapi_conn(**kwargs)
|
||||
|
||||
|
||||
@proxy_napalm_wrap
|
||||
def pyeapi_config(
|
||||
commands=None,
|
||||
|
@ -1839,7 +1779,11 @@ def config_diff_text(
|
|||
|
||||
@depends(HAS_SCP)
|
||||
def scp_get(
|
||||
remote_path, local_path="", recursive=False, preserve_times=False, **kwargs
|
||||
remote_path,
|
||||
local_path="",
|
||||
recursive=False,
|
||||
preserve_times=False,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
.. versionadded:: 2019.2.0
|
||||
|
|
|
@ -86,9 +86,7 @@ def _get_top_file_envs():
|
|||
else:
|
||||
envs = "base"
|
||||
except SaltRenderError as exc:
|
||||
raise CommandExecutionError(
|
||||
"Unable to render top file(s): {}".format(exc)
|
||||
)
|
||||
raise CommandExecutionError(f"Unable to render top file(s): {exc}")
|
||||
__context__["saltutil._top_file_envs"] = envs
|
||||
return envs
|
||||
|
||||
|
@ -164,7 +162,7 @@ def update(version=None):
|
|||
try:
|
||||
version = app.find_update()
|
||||
except urllib.error.URLError as exc:
|
||||
ret["_error"] = "Could not connect to update_url. Error: {}".format(exc)
|
||||
ret["_error"] = f"Could not connect to update_url. Error: {exc}"
|
||||
return ret
|
||||
if not version:
|
||||
ret["_error"] = "No updates available"
|
||||
|
@ -172,21 +170,21 @@ def update(version=None):
|
|||
try:
|
||||
app.fetch_version(version)
|
||||
except EskyVersionError as exc:
|
||||
ret["_error"] = "Unable to fetch version {}. Error: {}".format(version, exc)
|
||||
ret["_error"] = f"Unable to fetch version {version}. Error: {exc}"
|
||||
return ret
|
||||
try:
|
||||
app.install_version(version)
|
||||
except EskyVersionError as exc:
|
||||
ret["_error"] = "Unable to install version {}. Error: {}".format(version, exc)
|
||||
ret["_error"] = f"Unable to install version {version}. Error: {exc}"
|
||||
return ret
|
||||
try:
|
||||
app.cleanup()
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
ret["_error"] = "Unable to cleanup. Error: {}".format(exc)
|
||||
ret["_error"] = f"Unable to cleanup. Error: {exc}"
|
||||
restarted = {}
|
||||
for service in __opts__["update_restart_services"]:
|
||||
restarted[service] = __salt__["service.restart"](service)
|
||||
ret["comment"] = "Updated from {} to {}".format(oldversion, version)
|
||||
ret["comment"] = f"Updated from {oldversion} to {version}"
|
||||
ret["restarted"] = restarted
|
||||
return ret
|
||||
|
||||
|
@ -1016,6 +1014,55 @@ def sync_executors(
|
|||
return ret
|
||||
|
||||
|
||||
def sync_wrapper(
|
||||
saltenv=None, refresh=True, extmod_whitelist=None, extmod_blacklist=None
|
||||
):
|
||||
"""
|
||||
.. versionadded:: 3007.0
|
||||
|
||||
Sync salt-ssh wrapper modules from ``salt://_wrapper`` to the minion.
|
||||
|
||||
saltenv
|
||||
The fileserver environment from which to sync. To sync from more than
|
||||
one environment, pass a comma-separated list.
|
||||
|
||||
If not passed, then all environments configured in the :ref:`top files
|
||||
<states-top>` will be checked for wrappers to sync. If no top files
|
||||
are found, then the ``base`` environment will be synced.
|
||||
|
||||
refresh : True
|
||||
If ``True``, refresh the available wrapper modules on the minion.
|
||||
This refresh will be performed even if no wrappers are synced.
|
||||
Set to ``False`` to prevent this refresh.
|
||||
|
||||
extmod_whitelist : None
|
||||
comma-seperated list of modules to sync
|
||||
|
||||
extmod_blacklist : None
|
||||
comma-seperated list of modules to blacklist based on type
|
||||
|
||||
.. 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_wrapper
|
||||
salt '*' saltutil.sync_wrapper saltenv=dev
|
||||
salt '*' saltutil.sync_wrapper saltenv=base,dev
|
||||
"""
|
||||
if __opts__["file_client"] != "local":
|
||||
raise CommandExecutionError(
|
||||
"Wrapper modules can only be synced to masterless minions"
|
||||
)
|
||||
ret = _sync("wrapper", saltenv, extmod_whitelist, extmod_blacklist)
|
||||
if refresh:
|
||||
refresh_modules()
|
||||
return ret
|
||||
|
||||
|
||||
def sync_all(
|
||||
saltenv=None,
|
||||
refresh=True,
|
||||
|
@ -1105,6 +1152,9 @@ def sync_all(
|
|||
ret["matchers"] = sync_matchers(saltenv, False, extmod_whitelist, extmod_blacklist)
|
||||
if __opts__["file_client"] == "local":
|
||||
ret["pillar"] = sync_pillar(saltenv, False, extmod_whitelist, extmod_blacklist)
|
||||
ret["wrapper"] = sync_wrapper(
|
||||
saltenv, False, extmod_whitelist, extmod_blacklist
|
||||
)
|
||||
if refresh:
|
||||
# we don't need to call refresh_modules here because it's done by refresh_pillar
|
||||
refresh_pillar(clean_cache=clean_pillar_cache)
|
||||
|
@ -1402,7 +1452,7 @@ def find_cached_job(jid):
|
|||
" enable cache_jobs on this minion"
|
||||
)
|
||||
else:
|
||||
return "Local jobs cache directory {} not found".format(job_dir)
|
||||
return f"Local jobs cache directory {job_dir} not found"
|
||||
path = os.path.join(job_dir, "return.p")
|
||||
with salt.utils.files.fopen(path, "rb") as fp_:
|
||||
buf = fp_.read()
|
||||
|
@ -1604,7 +1654,7 @@ def _exec(
|
|||
kwarg,
|
||||
batch=False,
|
||||
subset=False,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
):
|
||||
fcn_ret = {}
|
||||
seen = 0
|
||||
|
@ -1660,7 +1710,7 @@ def cmd(
|
|||
ret="",
|
||||
kwarg=None,
|
||||
ssh=False,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
.. versionchanged:: 2017.7.0
|
||||
|
@ -1681,7 +1731,7 @@ def cmd(
|
|||
# if return is empty, we may have not used the right conf,
|
||||
# try with the 'minion relative master configuration counter part
|
||||
# if available
|
||||
master_cfgfile = "{}master".format(cfgfile[:-6]) # remove 'minion'
|
||||
master_cfgfile = f"{cfgfile[:-6]}master" # remove 'minion'
|
||||
if (
|
||||
not fcn_ret
|
||||
and cfgfile.endswith("{}{}".format(os.path.sep, "minion"))
|
||||
|
@ -1704,7 +1754,7 @@ def cmd_iter(
|
|||
ret="",
|
||||
kwarg=None,
|
||||
ssh=False,
|
||||
**kwargs
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
.. versionchanged:: 2017.7.0
|
||||
|
|
|
@ -48,6 +48,7 @@ import logging
|
|||
import salt.utils.platform
|
||||
import salt.utils.win_pwsh
|
||||
import salt.utils.win_reg
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -261,10 +262,15 @@ def remove(query=None, include_store=False, frameworks=False, deprovision_only=F
|
|||
frameworks=frameworks,
|
||||
bundles=True,
|
||||
)
|
||||
if bundle and bundle["IsBundle"]:
|
||||
log.debug(f'Found bundle: {bundle["PackageFullName"]}')
|
||||
remove_name = bundle["PackageFullName"]
|
||||
|
||||
if isinstance(bundle, list):
|
||||
# There may be multiple packages that match the query
|
||||
# Let's remove each one individually
|
||||
for item in bundle:
|
||||
remove_package(item)
|
||||
else:
|
||||
if bundle and bundle["IsBundle"]:
|
||||
log.debug(f'Found bundle: {bundle["PackageFullName"]}')
|
||||
remove_name = bundle["PackageFullName"]
|
||||
if deprovision_only:
|
||||
log.debug("Deprovisioning package: %s", remove_name)
|
||||
remove_cmd = (
|
||||
|
@ -273,7 +279,16 @@ def remove(query=None, include_store=False, frameworks=False, deprovision_only=F
|
|||
else:
|
||||
log.debug("Removing package: %s", remove_name)
|
||||
remove_cmd = f"Remove-AppxPackage -AllUsers -Package {remove_name}"
|
||||
salt.utils.win_pwsh.run_dict(remove_cmd)
|
||||
try:
|
||||
salt.utils.win_pwsh.run_dict(remove_cmd)
|
||||
except CommandExecutionError as exc:
|
||||
# Some packages may be in a partially updated state
|
||||
# In that case, there will be 2 entries with the same name
|
||||
# The old one will not have an installer and will throw an error
|
||||
# We should be safe just logging the message
|
||||
# This is really hard to replicate
|
||||
log.debug(f"There was an error removing package: {remove_name}")
|
||||
log.debug(exc)
|
||||
|
||||
if isinstance(packages, list):
|
||||
log.debug("Removing %s packages", len(packages))
|
||||
|
|
|
@ -439,11 +439,6 @@ def _decrypt_ciphertext(cipher):
|
|||
decrypt_error,
|
||||
)
|
||||
)
|
||||
else:
|
||||
salt.utils.versions.warn_until(
|
||||
3007,
|
||||
"After the Chlorine release of Salt, gpg_decrypt_must_succeed will default to True.",
|
||||
)
|
||||
return cipher
|
||||
else:
|
||||
if __opts__.get("gpg_cache"):
|
||||
|
|
|
@ -146,6 +146,11 @@ def sync_all(saltenv="base", extmod_whitelist=None, extmod_blacklist=None):
|
|||
extmod_whitelist=extmod_whitelist,
|
||||
extmod_blacklist=extmod_blacklist,
|
||||
)
|
||||
ret["wrapper"] = sync_wrapper(
|
||||
saltenv=saltenv,
|
||||
extmod_whitelist=extmod_whitelist,
|
||||
extmod_blacklist=extmod_blacklist,
|
||||
)
|
||||
ret["roster"] = sync_roster(
|
||||
saltenv=saltenv,
|
||||
extmod_whitelist=extmod_whitelist,
|
||||
|
@ -835,3 +840,34 @@ def sync_executors(saltenv="base", extmod_whitelist=None, extmod_blacklist=None)
|
|||
extmod_whitelist=extmod_whitelist,
|
||||
extmod_blacklist=extmod_blacklist,
|
||||
)[0]
|
||||
|
||||
|
||||
def sync_wrapper(saltenv="base", extmod_whitelist=None, extmod_blacklist=None):
|
||||
"""
|
||||
.. versionadded:: 3007.0
|
||||
|
||||
Sync salt-ssh wrapper modules from ``salt://_wrapper`` to the master.
|
||||
|
||||
saltenv : base
|
||||
The fileserver environment from which to sync. To sync from more than
|
||||
one environment, pass a comma-separated list.
|
||||
|
||||
extmod_whitelist : None
|
||||
comma-seperated list of modules to sync
|
||||
|
||||
extmod_blacklist : None
|
||||
comma-seperated list of modules to blacklist based on type
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt-run saltutil.sync_wrapper
|
||||
"""
|
||||
return salt.utils.extmods.sync(
|
||||
__opts__,
|
||||
"wrapper",
|
||||
saltenv=saltenv,
|
||||
extmod_whitelist=extmod_whitelist,
|
||||
extmod_blacklist=extmod_blacklist,
|
||||
)[0]
|
||||
|
|
|
@ -29,18 +29,18 @@ def _sync_single(name, module, **kwargs):
|
|||
|
||||
if __opts__["test"]:
|
||||
ret["result"] = None
|
||||
ret["comment"] = "saltutil.sync_{} would have been run".format(module)
|
||||
ret["comment"] = f"saltutil.sync_{module} would have been run"
|
||||
return ret
|
||||
|
||||
try:
|
||||
sync_status = __salt__["saltutil.sync_{}".format(module)](**kwargs)
|
||||
sync_status = __salt__[f"saltutil.sync_{module}"](**kwargs)
|
||||
if sync_status:
|
||||
ret["changes"][module] = sync_status
|
||||
ret["comment"] = "Updated {}.".format(module)
|
||||
ret["comment"] = f"Updated {module}."
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
log.error("Failed to run saltutil.sync_%s: %s", module, e)
|
||||
ret["result"] = False
|
||||
ret["comment"] = "Failed to run sync_{}: {}".format(module, e)
|
||||
ret["comment"] = f"Failed to run sync_{module}: {e}"
|
||||
return ret
|
||||
|
||||
if not ret["changes"]:
|
||||
|
@ -76,7 +76,7 @@ def sync_all(name, **kwargs):
|
|||
except Exception as e: # pylint: disable=broad-except
|
||||
log.error("Failed to run saltutil.sync_all: %s", e)
|
||||
ret["result"] = False
|
||||
ret["comment"] = "Failed to run sync_all: {}".format(e)
|
||||
ret["comment"] = f"Failed to run sync_all: {e}"
|
||||
return ret
|
||||
|
||||
if not ret["changes"]:
|
||||
|
@ -349,3 +349,19 @@ def sync_serializers(name, **kwargs):
|
|||
- refresh: True
|
||||
"""
|
||||
return _sync_single(name, "serializers", **kwargs)
|
||||
|
||||
|
||||
def sync_wrapper(name, **kwargs):
|
||||
"""
|
||||
.. versionadded:: 3007.0
|
||||
|
||||
Performs the same task as saltutil.sync_wrapper module
|
||||
See :mod:`saltutil module for full list of options <salt.modules.saltutil>`
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
sync_everything:
|
||||
saltutil.sync_wrapper:
|
||||
- refresh: True
|
||||
"""
|
||||
return _sync_single(name, "wrapper", **kwargs)
|
||||
|
|
|
@ -31,6 +31,7 @@ def get_module_types():
|
|||
"tokens",
|
||||
"serializers",
|
||||
"executors",
|
||||
"wrapper",
|
||||
"roster",
|
||||
]
|
||||
return module_types
|
||||
|
@ -66,6 +67,7 @@ def module_sync_functions():
|
|||
"tokens": "eauth_tokens",
|
||||
"serializers": "serializers",
|
||||
"executors": "executors",
|
||||
"wrapper": "wrapper",
|
||||
"roster": "roster",
|
||||
}
|
||||
|
||||
|
@ -76,7 +78,7 @@ def test_sync(
|
|||
"""
|
||||
Ensure modules are synced when various sync functions are called
|
||||
"""
|
||||
module_name = "hello_sync_{}".format(module_type)
|
||||
module_name = f"hello_sync_{module_type}"
|
||||
module_contents = """
|
||||
def __virtual__():
|
||||
return "hello"
|
||||
|
@ -85,17 +87,17 @@ def world():
|
|||
return "world"
|
||||
"""
|
||||
|
||||
test_moduledir = salt_master.state_tree.base.paths[0] / "_{}".format(module_type)
|
||||
test_moduledir = salt_master.state_tree.base.paths[0] / f"_{module_type}"
|
||||
test_moduledir.mkdir(parents=True, exist_ok=True)
|
||||
module_tempfile = salt_master.state_tree.base.temp_file(
|
||||
"_{}/{}.py".format(module_type, module_name), module_contents
|
||||
f"_{module_type}/{module_name}.py", module_contents
|
||||
)
|
||||
|
||||
with module_tempfile, test_moduledir:
|
||||
salt_cmd = "saltutil.sync_{}".format(module_sync_functions[module_type])
|
||||
salt_cmd = f"saltutil.sync_{module_sync_functions[module_type]}"
|
||||
ret = salt_run_cli.run(salt_cmd)
|
||||
assert ret.returncode == 0
|
||||
assert "{}.hello".format(module_type) in ret.stdout
|
||||
assert f"{module_type}.hello" in ret.stdout
|
||||
|
||||
|
||||
def _write_module_dir_and_file(module_type, salt_minion, salt_master):
|
||||
|
@ -111,11 +113,11 @@ def world():
|
|||
return "world"
|
||||
"""
|
||||
|
||||
test_moduledir = salt_master.state_tree.base.paths[0] / "_{}".format(module_type)
|
||||
test_moduledir = salt_master.state_tree.base.paths[0] / f"_{module_type}"
|
||||
test_moduledir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
module_tempfile = salt_master.state_tree.base.temp_file(
|
||||
"_{}/{}.py".format(module_type, module_name), module_contents
|
||||
f"_{module_type}/{module_name}.py", module_contents
|
||||
)
|
||||
|
||||
return module_tempfile
|
||||
|
@ -139,4 +141,4 @@ def test_sync_all(salt_run_cli, salt_minion, salt_master):
|
|||
|
||||
assert ret.returncode == 0
|
||||
for module_type in get_module_types():
|
||||
assert "{}.hello".format(module_type) in ret.stdout
|
||||
assert f"{module_type}.hello" in ret.stdout
|
||||
|
|
|
@ -63,6 +63,30 @@ def _state_tree(salt_master, tmp_path):
|
|||
yield
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def custom_wrapper(salt_run_cli, base_env_state_tree_root_dir):
|
||||
module_contents = r"""\
|
||||
def __virtual__():
|
||||
return "grains_custom"
|
||||
|
||||
def items():
|
||||
return __grains__.value()
|
||||
"""
|
||||
module_dir = base_env_state_tree_root_dir / "_wrapper"
|
||||
module_tempfile = pytest.helpers.temp_file(
|
||||
"grains_custom.py", module_contents, module_dir
|
||||
)
|
||||
try:
|
||||
with module_tempfile:
|
||||
ret = salt_run_cli.run("saltutil.sync_wrapper")
|
||||
assert ret.returncode == 0
|
||||
assert "wrapper.grains_custom" in ret.data
|
||||
yield
|
||||
finally:
|
||||
ret = salt_run_cli.run("saltutil.sync_wrapper")
|
||||
assert ret.returncode == 0
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("_state_tree")
|
||||
def test_state_apply(salt_ssh_cli):
|
||||
ret = salt_ssh_cli.run("state.apply", "core")
|
||||
|
@ -77,3 +101,14 @@ def test_state_highstate(salt_ssh_cli):
|
|||
assert ret.returncode == 0
|
||||
state_result = StateResult(ret.data)
|
||||
assert state_result.result is True
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("custom_wrapper")
|
||||
def test_custom_wrapper(salt_ssh_cli):
|
||||
ret = salt_ssh_cli.run(
|
||||
"grains_custom.items",
|
||||
)
|
||||
assert ret.returncode == 0
|
||||
assert ret.data
|
||||
assert "id" in ret.data
|
||||
assert ret.data["id"] in ("localhost", "127.0.0.1")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import pytest
|
||||
|
||||
import salt.modules.win_appx as win_appx
|
||||
from tests.support.mock import MagicMock, patch
|
||||
from tests.support.mock import MagicMock, call, patch
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.windows_whitelisted,
|
||||
|
@ -136,6 +136,51 @@ def test_remove():
|
|||
mock_run_dict.assert_called_with(cmd)
|
||||
|
||||
|
||||
def test_remove_duplicate():
|
||||
mock_run_dict = MagicMock()
|
||||
mock_list_return_1 = [
|
||||
{
|
||||
"Name": "Microsoft.BingWeather",
|
||||
"PackageFullName": "Microsoft.BingWeather_full_name_1",
|
||||
"IsBundle": False,
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.BingWeather",
|
||||
"PackageFullName": "Microsoft.BingWeather_full_name_2",
|
||||
"IsBundle": False,
|
||||
},
|
||||
]
|
||||
mock_list_return_2 = [
|
||||
{
|
||||
"Name": "Microsoft.BingWeather",
|
||||
"PackageFullName": "Microsoft.BingWeather_full_name_1",
|
||||
"IsBundle": True,
|
||||
},
|
||||
{
|
||||
"Name": "Microsoft.BingWeather",
|
||||
"PackageFullName": "Microsoft.BingWeather_full_name_2",
|
||||
"IsBundle": True,
|
||||
},
|
||||
]
|
||||
mock_list_return_3 = [
|
||||
{
|
||||
"Name": "Microsoft.BingWeather",
|
||||
"PackageFullName": "Microsoft.BingWeather_full_name_2",
|
||||
"IsBundle": True,
|
||||
},
|
||||
]
|
||||
mock_list = MagicMock(
|
||||
side_effect=[mock_list_return_1, mock_list_return_2, mock_list_return_3]
|
||||
)
|
||||
with patch("salt.utils.win_pwsh.run_dict", mock_run_dict), patch.object(
|
||||
win_appx, "list_", mock_list
|
||||
):
|
||||
assert win_appx.remove("*bingweather*") is True
|
||||
cmd_1 = "Remove-AppxPackage -AllUsers -Package Microsoft.BingWeather_full_name_1"
|
||||
cmd_2 = "Remove-AppxPackage -AllUsers -Package Microsoft.BingWeather_full_name_2"
|
||||
mock_run_dict.assert_has_calls([call(cmd_1), call(cmd_2)])
|
||||
|
||||
|
||||
def test_remove_deprovision_only():
|
||||
mock_run_dict = MagicMock()
|
||||
mock_list_return = {
|
||||
|
|
|
@ -36,6 +36,7 @@ def test_saltutil_sync_all_nochange():
|
|||
"pillar": [],
|
||||
"matchers": [],
|
||||
"serializers": [],
|
||||
"wrapper": [],
|
||||
}
|
||||
state_id = "somename"
|
||||
state_result = {
|
||||
|
@ -71,6 +72,7 @@ def test_saltutil_sync_all_test():
|
|||
"pillar": [],
|
||||
"matchers": [],
|
||||
"serializers": [],
|
||||
"wrapper": [],
|
||||
}
|
||||
state_id = "somename"
|
||||
state_result = {
|
||||
|
@ -107,6 +109,7 @@ def test_saltutil_sync_all_change():
|
|||
"pillar": [],
|
||||
"matchers": [],
|
||||
"serializers": [],
|
||||
"wrapper": [],
|
||||
}
|
||||
state_id = "somename"
|
||||
state_result = {
|
||||
|
@ -139,4 +142,4 @@ def test_saltutil_sync_states_should_match_saltutil_module():
|
|||
for fn in module_functions:
|
||||
assert (
|
||||
fn in state_functions
|
||||
), "modules.saltutil.{} has no matching state in states.saltutil".format(fn)
|
||||
), f"modules.saltutil.{fn} has no matching state in states.saltutil"
|
||||
|
|
Loading…
Add table
Reference in a new issue