mirror of
https://github.com/saltstack/salt.git
synced 2025-04-17 10:10:20 +00:00
Merge 3007.x into master
This commit is contained in:
commit
0ecde50005
22 changed files with 646 additions and 303 deletions
3
changelog/64722.added.md
Normal file
3
changelog/64722.added.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
Added the ability to pass a version of chocolatey to install to the
|
||||
chocolatey.bootstrap function. Also added states to bootstrap and
|
||||
unbootstrap chocolatey.
|
|
@ -74,15 +74,15 @@ if ( (Get-WindowsOptionalFeature -Online -FeatureName "NetFx3").State -eq "Enabl
|
|||
#-------------------------------------------------------------------------------
|
||||
|
||||
Write-Host "Looking for Wix Toolset: " -NoNewline
|
||||
$guid_64 = "{A2D09E18-32F8-4E34-946A-33AC8C8303E9}"
|
||||
$guid_32 = "{00A0C4F8-9F6C-40FB-A02D-3EAE1D7FD352}"
|
||||
$guid_64 = "{F0F0AEBC-3FF8-46E4-80EC-625C2CCF241D}"
|
||||
$guid_32 = "{87475CA3-0418-47E5-A51F-DE5BD3D0D9FB}"
|
||||
if ( (ProductcodeExists $guid_64) -or (ProductcodeExists $guid_32) ) {
|
||||
Write-Result "Success" -ForegroundColor Green
|
||||
} else {
|
||||
Write-Result "Missing" -ForegroundColor Yellow
|
||||
|
||||
Write-Host "Downloading Wix Toolset: " -NoNewline
|
||||
$url = "https://github.com/wixtoolset/wix3/releases/download/wix314rtm/wix314.exe"
|
||||
$url = "https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314.exe"
|
||||
$file = "$env:TEMP\wix_installer.exe"
|
||||
Invoke-WebRequest -Uri $url -OutFile "$file"
|
||||
if ( Test-Path -Path "$file" ) {
|
||||
|
|
|
@ -533,74 +533,6 @@ Section -copy_prereqs
|
|||
File /r "..\..\prereqs\"
|
||||
SectionEnd
|
||||
|
||||
# Check if the Windows 10 Universal C Runtime (KB2999226) is installed. Python
|
||||
# 3 needs the updated ucrt on Windows 8.1/2012R2 and lower. They are installed
|
||||
# via KB2999226, but we're not going to patch the system here. Instead, we're
|
||||
# going to copy the .dll files to the \salt\bin directory
|
||||
Section -install_ucrt
|
||||
|
||||
Var /GLOBAL UcrtFileName
|
||||
|
||||
# Get the Major.Minor version Number
|
||||
# Windows 10 introduced CurrentMajorVersionNumber
|
||||
ReadRegStr $R0 HKLM "SOFTWARE\Microsoft\Windows NT\CurrentVersion" \
|
||||
CurrentMajorVersionNumber
|
||||
|
||||
# Windows 10/2016 will return a value here, skip to the end if returned
|
||||
StrCmp $R0 '' lbl_needs_ucrt 0
|
||||
|
||||
# Found Windows 10
|
||||
detailPrint "KB2999226 does not apply to this machine"
|
||||
goto lbl_done
|
||||
|
||||
lbl_needs_ucrt:
|
||||
# UCRT only needed on Windows Server 2012R2/Windows 8.1 and below. The
|
||||
# first ReadRegStr command above should have skipped to lbl_done if on
|
||||
# Windows 10 box
|
||||
|
||||
# Is the update already installed
|
||||
ClearErrors
|
||||
|
||||
# Use WMI to check if it's installed
|
||||
detailPrint "Checking for existing UCRT (KB2999226) installation"
|
||||
nsExec::ExecToStack 'cmd /q /c wmic qfe get hotfixid | findstr "^KB2999226"'
|
||||
# Clean up the stack
|
||||
Pop $R0 # Gets the ErrorCode
|
||||
Pop $R1 # Gets the stdout, which should be KB2999226 if it's installed
|
||||
|
||||
# If it returned KB2999226 it's already installed
|
||||
StrCmp $R1 'KB2999226' lbl_done
|
||||
|
||||
detailPrint "UCRT (KB2999226) not found"
|
||||
|
||||
# Use RunningX64 here to get the Architecture for the system running the
|
||||
# installer.
|
||||
${If} ${RunningX64}
|
||||
StrCpy $UcrtFileName "ucrt_x64.zip"
|
||||
${Else}
|
||||
StrCpy $UcrtFileName "ucrt_x86.zip"
|
||||
${EndIf}
|
||||
|
||||
ClearErrors
|
||||
|
||||
detailPrint "Unzipping UCRT dll files to $INSTDIR\Scripts"
|
||||
CreateDirectory $INSTDIR\Scripts
|
||||
nsisunz::UnzipToLog "$PLUGINSDIR\$UcrtFileName" "$INSTDIR\Scripts"
|
||||
|
||||
# Clean up the stack
|
||||
Pop $R0 # Get Error
|
||||
|
||||
${IfNot} $R0 == "success"
|
||||
detailPrint "error: $R0"
|
||||
Sleep 3000
|
||||
${Else}
|
||||
detailPrint "UCRT dll files copied successfully"
|
||||
${EndIf}
|
||||
|
||||
lbl_done:
|
||||
|
||||
SectionEnd
|
||||
|
||||
|
||||
# Check and install Visual C++ redist 2022 packages
|
||||
# Hidden section (-) to install VCRedist
|
||||
|
|
|
@ -14,6 +14,7 @@ from requests.structures import CaseInsensitiveDict
|
|||
|
||||
import salt.utils.data
|
||||
import salt.utils.platform
|
||||
import salt.utils.win_dotnet
|
||||
from salt.exceptions import (
|
||||
CommandExecutionError,
|
||||
CommandNotFoundError,
|
||||
|
@ -89,10 +90,22 @@ def _no_progress():
|
|||
return __context__["chocolatey._no_progress"]
|
||||
|
||||
|
||||
def _find_chocolatey():
|
||||
def _find_chocolatey(refresh=False):
|
||||
"""
|
||||
Returns the full path to chocolatey.bat on the host.
|
||||
Returns the full path to the chocolatey binary on the host. If found, the
|
||||
location is cached in ``__context__``.
|
||||
|
||||
Args:
|
||||
|
||||
refresh (bool):
|
||||
Refresh the cached location of the chocolatey binary in
|
||||
``__context__``
|
||||
|
||||
.. versionadded:: 3007.1
|
||||
"""
|
||||
if refresh:
|
||||
__context__.pop("chocolatey._path", False)
|
||||
|
||||
# Check context
|
||||
if "chocolatey._path" in __context__:
|
||||
return __context__["chocolatey._path"]
|
||||
|
@ -126,28 +139,37 @@ def _find_chocolatey():
|
|||
raise CommandExecutionError(err)
|
||||
|
||||
|
||||
def chocolatey_version():
|
||||
def chocolatey_version(refresh=False):
|
||||
"""
|
||||
Returns the version of Chocolatey installed on the minion.
|
||||
|
||||
Args:
|
||||
|
||||
refresh (bool):
|
||||
Refresh the cached version of chocolatey
|
||||
|
||||
.. versionadded:: 3007.1
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' chocolatey.chocolatey_version
|
||||
"""
|
||||
if refresh:
|
||||
__context__.pop("chocolatey._version", False)
|
||||
|
||||
if "chocolatey._version" in __context__:
|
||||
return __context__["chocolatey._version"]
|
||||
|
||||
cmd = [_find_chocolatey()]
|
||||
cmd.append("-v")
|
||||
cmd = [_find_chocolatey(refresh=refresh), "-v"]
|
||||
out = __salt__["cmd.run"](cmd, python_shell=False)
|
||||
__context__["chocolatey._version"] = out
|
||||
|
||||
return __context__["chocolatey._version"]
|
||||
|
||||
|
||||
def bootstrap(force=False, source=None):
|
||||
def bootstrap(force=False, source=None, version=None):
|
||||
"""
|
||||
Download and install the latest version of the Chocolatey package manager
|
||||
via the official bootstrap.
|
||||
|
@ -166,6 +188,11 @@ def bootstrap(force=False, source=None):
|
|||
and .NET requirements must already be met on the target. This shouldn't
|
||||
be a problem on Windows versions 2012/8 and later
|
||||
|
||||
.. note::
|
||||
If you're installing chocolatey version 2.0+ the system requires .NET
|
||||
4.8. Installing this requires a reboot, therefore this module will not
|
||||
automatically install .NET 4.8.
|
||||
|
||||
Args:
|
||||
|
||||
force (bool):
|
||||
|
@ -182,6 +209,12 @@ def bootstrap(force=False, source=None):
|
|||
|
||||
.. versionadded:: 3001
|
||||
|
||||
version (str):
|
||||
The version of chocolatey to install. The latest version is
|
||||
installed if this value is ``None``. Default is ``None``
|
||||
|
||||
.. versionadded:: 3007.1
|
||||
|
||||
Returns:
|
||||
str: The stdout of the Chocolatey installation script
|
||||
|
||||
|
@ -198,6 +231,9 @@ def bootstrap(force=False, source=None):
|
|||
|
||||
# To bootstrap Chocolatey from a file on C:\\Temp
|
||||
salt '*' chocolatey.bootstrap source=C:\\Temp\\chocolatey.nupkg
|
||||
|
||||
# To bootstrap Chocolatey version 1.4.0
|
||||
salt '*' chocolatey.bootstrap version=1.4.0
|
||||
"""
|
||||
# Check if Chocolatey is already present in the path
|
||||
try:
|
||||
|
@ -272,7 +308,7 @@ def bootstrap(force=False, source=None):
|
|||
# Check that .NET v4.0+ is installed
|
||||
# Windows 7 / Windows Server 2008 R2 and below do not come with at least
|
||||
# .NET v4.0 installed
|
||||
if not __utils__["dotnet.version_at_least"](version="4"):
|
||||
if not salt.utils.win_dotnet.version_at_least(version="4"):
|
||||
# It took until .NET v4.0 for Microsoft got the hang of making
|
||||
# installers, this should work under any version of Windows
|
||||
url = "http://download.microsoft.com/download/1/B/E/1BE39E79-7E39-46A3-96FF-047F95396215/dotNetFx40_Full_setup.exe"
|
||||
|
@ -336,10 +372,21 @@ def bootstrap(force=False, source=None):
|
|||
f"Failed to find Chocolatey installation script: {script}"
|
||||
)
|
||||
|
||||
# You tell the chocolatey install script which version to install by setting
|
||||
# an environment variable
|
||||
if version:
|
||||
env = {"chocolateyVersion": version}
|
||||
else:
|
||||
env = None
|
||||
|
||||
# Run the Chocolatey bootstrap
|
||||
log.debug("Installing Chocolatey: %s", script)
|
||||
result = __salt__["cmd.script"](
|
||||
script, cwd=os.path.dirname(script), shell="powershell", python_shell=True
|
||||
source=script,
|
||||
cwd=os.path.dirname(script),
|
||||
shell="powershell",
|
||||
python_shell=True,
|
||||
env=env,
|
||||
)
|
||||
if result["retcode"] != 0:
|
||||
err = "Bootstrapping Chocolatey failed: {}".format(result["stderr"])
|
||||
|
@ -368,24 +415,24 @@ def unbootstrap():
|
|||
salt * chocolatey.unbootstrap
|
||||
"""
|
||||
removed = []
|
||||
|
||||
# Delete the Chocolatey directory
|
||||
choco_dir = os.environ.get("ChocolateyInstall", False)
|
||||
if choco_dir:
|
||||
if os.path.exists(choco_dir):
|
||||
log.debug("Removing Chocolatey directory: %s", choco_dir)
|
||||
__salt__["file.remove"](path=choco_dir, force=True)
|
||||
removed.append(f"Removed Directory: {choco_dir}")
|
||||
else:
|
||||
known_paths = [
|
||||
known_paths = []
|
||||
# Get the install location from the registry first
|
||||
if os.environ.get("ChocolateyInstall", False):
|
||||
known_paths.append(os.environ.get("ChocolateyInstall"))
|
||||
known_paths.extend(
|
||||
[
|
||||
# Default location
|
||||
os.path.join(os.environ.get("ProgramData"), "Chocolatey"),
|
||||
# Old default location
|
||||
os.path.join(os.environ.get("SystemDrive"), "Chocolatey"),
|
||||
]
|
||||
for path in known_paths:
|
||||
if os.path.exists(path):
|
||||
log.debug("Removing Chocolatey directory: %s", path)
|
||||
__salt__["file.remove"](path=path, force=True)
|
||||
removed.append(f"Removed Directory: {path}")
|
||||
)
|
||||
# Delete all known Chocolatey directories
|
||||
for path in known_paths:
|
||||
if os.path.exists(path):
|
||||
log.debug("Removing Chocolatey directory: %s", path)
|
||||
__salt__["file.remove"](path=path, force=True)
|
||||
removed.append(f"Removed Directory: {path}")
|
||||
|
||||
# Delete all Chocolatey environment variables
|
||||
for env_var in __salt__["environ.items"]():
|
||||
|
|
|
@ -12,6 +12,7 @@ import errno
|
|||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import pathlib
|
||||
import stat
|
||||
import sys
|
||||
import tempfile
|
||||
|
@ -1345,40 +1346,36 @@ def remove(path, force=False):
|
|||
# Symlinks. The shutil.rmtree function will remove the contents of
|
||||
# the Symlink source in windows.
|
||||
|
||||
path = os.path.expanduser(path)
|
||||
path = pathlib.Path(os.path.expanduser(path))
|
||||
|
||||
if not os.path.isabs(path):
|
||||
if not path.is_absolute():
|
||||
raise SaltInvocationError(f"File path must be absolute: {path}")
|
||||
|
||||
# Does the file/folder exists
|
||||
if not os.path.exists(path) and not is_link(path):
|
||||
if not path.exists() and not path.is_symlink():
|
||||
raise CommandExecutionError(f"Path not found: {path}")
|
||||
|
||||
# Remove ReadOnly Attribute
|
||||
file_attributes = win32api.GetFileAttributes(str(path))
|
||||
if force:
|
||||
# Get current file attributes
|
||||
file_attributes = win32api.GetFileAttributes(path)
|
||||
win32api.SetFileAttributes(path, win32con.FILE_ATTRIBUTE_NORMAL)
|
||||
win32api.SetFileAttributes(str(path), win32con.FILE_ATTRIBUTE_NORMAL)
|
||||
|
||||
try:
|
||||
if os.path.isfile(path):
|
||||
if path.is_file() or path.is_symlink():
|
||||
# A file and a symlinked file are removed the same way
|
||||
os.remove(path)
|
||||
elif is_link(path):
|
||||
# If it's a symlink directory, use the rmdir command
|
||||
os.rmdir(path)
|
||||
path.unlink()
|
||||
else:
|
||||
for name in os.listdir(path):
|
||||
item = f"{path}\\{name}"
|
||||
# If its a normal directory, recurse to remove it's contents
|
||||
remove(item, force)
|
||||
for child in path.iterdir():
|
||||
# If it's a normal directory, recurse to remove its contents
|
||||
remove(str(child), force)
|
||||
|
||||
# rmdir will work now because the directory is empty
|
||||
os.rmdir(path)
|
||||
path.rmdir()
|
||||
except OSError as exc:
|
||||
if force:
|
||||
# Reset attributes to the original if delete fails.
|
||||
win32api.SetFileAttributes(path, file_attributes)
|
||||
win32api.SetFileAttributes(str(path), file_attributes)
|
||||
raise CommandExecutionError(f"Could not remove '{path}': {exc}")
|
||||
|
||||
return True
|
||||
|
|
|
@ -11,7 +11,7 @@ Manage Windows Packages using Chocolatey
|
|||
|
||||
import salt.utils.data
|
||||
import salt.utils.versions
|
||||
from salt.exceptions import SaltInvocationError
|
||||
from salt.exceptions import CommandExecutionError, SaltInvocationError
|
||||
|
||||
|
||||
def __virtual__():
|
||||
|
@ -513,3 +513,182 @@ def source_present(
|
|||
ret["changes"] = salt.utils.data.compare_dicts(pre_install, post_install)
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def bootstrapped(name, force=False, source=None, version=None):
|
||||
"""
|
||||
.. versionadded:: 3007.1
|
||||
|
||||
Ensure chocolatey is installed on the system.
|
||||
|
||||
You can't upgrade an existing installation with this state. You must use
|
||||
chocolatey to upgrade chocolatey.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
choco upgrade chocolatey --version 2.2.0
|
||||
|
||||
Args:
|
||||
|
||||
name (str):
|
||||
The name of the state that installs chocolatey. Required for all
|
||||
states. This is ignored.
|
||||
|
||||
force (bool):
|
||||
Run the bootstrap process even if Chocolatey is found in the path.
|
||||
|
||||
.. note::
|
||||
If chocolatey is already installed this will just re-run the
|
||||
install script over the existing version. The ``version``
|
||||
parameter is ignored.
|
||||
|
||||
source (str):
|
||||
The location of the ``.nupkg`` file or ``.ps1`` file to run from an
|
||||
alternate location. This can be one of the following types of URLs:
|
||||
|
||||
- salt://
|
||||
- http(s)://
|
||||
- ftp://
|
||||
- file:// - A local file on the system
|
||||
|
||||
version (str):
|
||||
The version of chocolatey to install. The latest version is
|
||||
installed if this value is ``None``. Default is ``None``
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# Bootstrap the latest version of chocolatey
|
||||
bootstrap_chocolatey:
|
||||
chocolatey.bootstrapped
|
||||
|
||||
# Bootstrap the latest version of chocolatey
|
||||
# If chocolatey is already present, re-run the install script
|
||||
bootstrap_chocolatey:
|
||||
chocolatey.bootstrapped:
|
||||
- force: True
|
||||
|
||||
# Bootstrap Chocolatey version 1.4.0
|
||||
bootstrap_chocolatey:
|
||||
chocolatey.bootstrapped:
|
||||
- version: 1.4.0
|
||||
|
||||
# Bootstrap Chocolatey from a local file
|
||||
bootstrap_chocolatey:
|
||||
chocolatey.bootstrapped:
|
||||
- source: C:\\Temp\\chocolatey.nupkg
|
||||
|
||||
# Bootstrap Chocolatey from a file on the salt master
|
||||
bootstrap_chocolatey:
|
||||
chocolatey.bootstrapped:
|
||||
- source: salt://Temp/chocolatey.nupkg
|
||||
"""
|
||||
ret = {"name": name, "result": True, "changes": {}, "comment": ""}
|
||||
|
||||
try:
|
||||
old = __salt__["chocolatey.chocolatey_version"]()
|
||||
except CommandExecutionError:
|
||||
old = None
|
||||
|
||||
# Try to predict what will happen
|
||||
if old:
|
||||
if force:
|
||||
ret["comment"] = (
|
||||
f"Chocolatey {old} will be reinstalled\n"
|
||||
'Use "choco upgrade chocolatey --version 2.1.0" to change the version'
|
||||
)
|
||||
else:
|
||||
# You can't upgrade chocolatey using the install script, you have to use
|
||||
# chocolatey itself
|
||||
ret["comment"] = (
|
||||
f"Chocolatey {old} is already installed.\n"
|
||||
'Use "choco upgrade chocolatey --version 2.1.0" to change the version'
|
||||
)
|
||||
return ret
|
||||
|
||||
else:
|
||||
if version is None:
|
||||
ret["comment"] = "The latest version of Chocolatey will be installed"
|
||||
else:
|
||||
ret["comment"] = f"Chocolatey {version} will be installed"
|
||||
|
||||
if __opts__["test"]:
|
||||
ret["result"] = None
|
||||
return ret
|
||||
|
||||
__salt__["chocolatey.bootstrap"](force=force, source=source, version=version)
|
||||
|
||||
try:
|
||||
new = __salt__["chocolatey.chocolatey_version"](refresh=True)
|
||||
except CommandExecutionError:
|
||||
new = None
|
||||
|
||||
if new is None:
|
||||
ret["comment"] = f"Failed to install chocolatey {new}"
|
||||
ret["result"] = False
|
||||
else:
|
||||
if salt.utils.versions.version_cmp(old, new) == 0:
|
||||
ret["comment"] = f"Re-installed chocolatey {new}"
|
||||
else:
|
||||
ret["comment"] = f"Installed chocolatey {new}"
|
||||
ret["changes"] = {"old": old, "new": new}
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def unbootstrapped(name):
|
||||
"""
|
||||
.. versionadded:: 3007.1
|
||||
|
||||
Ensure chocolatey is removed from the system.
|
||||
|
||||
Args:
|
||||
|
||||
name (str):
|
||||
The name of the state that uninstalls chocolatey. Required for all
|
||||
states. This is ignored.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
# Uninstall chocolatey
|
||||
uninstall_chocolatey:
|
||||
chocolatey.unbootstrapped
|
||||
|
||||
"""
|
||||
ret = {"name": name, "result": True, "changes": {}, "comment": ""}
|
||||
|
||||
try:
|
||||
old = __salt__["chocolatey.chocolatey_version"]()
|
||||
except CommandExecutionError:
|
||||
old = None
|
||||
|
||||
if old is None:
|
||||
ret["comment"] = "Chocolatey not found on this system"
|
||||
return ret
|
||||
|
||||
ret["comment"] = f"Chocolatey {old} will be removed"
|
||||
|
||||
if __opts__["test"]:
|
||||
ret["result"] = None
|
||||
return ret
|
||||
|
||||
__salt__["chocolatey.unbootstrap"]()
|
||||
|
||||
try:
|
||||
new = __salt__["chocolatey.chocolatey_version"](refresh=True)
|
||||
except CommandExecutionError:
|
||||
new = None
|
||||
|
||||
if new is None:
|
||||
ret["comment"] = f"Uninstalled chocolatey {old}"
|
||||
ret["changes"] = {"new": new, "old": old}
|
||||
else:
|
||||
ret["comment"] = f"Failed to uninstall chocolatey {old}\nFound version {new}"
|
||||
ret["result"] = False
|
||||
|
||||
return ret
|
||||
|
|
62
tests/pytests/functional/modules/test_chocolatey.py
Normal file
62
tests/pytests/functional/modules/test_chocolatey.py
Normal file
|
@ -0,0 +1,62 @@
|
|||
import pytest
|
||||
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.destructive_test,
|
||||
pytest.mark.skip_unless_on_windows,
|
||||
pytest.mark.slow_test,
|
||||
pytest.mark.windows_whitelisted,
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def chocolatey(modules):
|
||||
return modules.chocolatey
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def clean(chocolatey):
|
||||
result = chocolatey.unbootstrap()
|
||||
|
||||
# Try to get the new version, should throw an error
|
||||
try:
|
||||
chocolatey_version = chocolatey.chocolatey_version(refresh=True)
|
||||
except CommandExecutionError:
|
||||
chocolatey_version = None
|
||||
|
||||
# Assert the chocolatey is not installed
|
||||
assert chocolatey_version is None
|
||||
try:
|
||||
# We're yielding "result" here so we can see any problems with
|
||||
# unbootstrap if the test fails
|
||||
yield result
|
||||
finally:
|
||||
try:
|
||||
# If chocolatey is not installed, this will throw an error
|
||||
chocolatey.chocolatey_version(refresh=True)
|
||||
# If we get this far, chocolatey is installed... let's uninstall
|
||||
chocolatey.unbootstrap()
|
||||
except CommandExecutionError:
|
||||
pass
|
||||
|
||||
|
||||
def test_bootstrap(chocolatey, clean):
|
||||
# We're defining "result" here to see the output of the bootstrap function
|
||||
# if the test fails
|
||||
result = chocolatey.bootstrap()
|
||||
# Let's run it outside the try/except to see what the error is
|
||||
try:
|
||||
chocolatey_version = chocolatey.chocolatey_version(refresh=True)
|
||||
except CommandExecutionError:
|
||||
chocolatey_version = None
|
||||
assert chocolatey_version is not None
|
||||
|
||||
|
||||
def test_bootstrap_version(chocolatey, clean):
|
||||
chocolatey.bootstrap(version="1.4.0")
|
||||
try:
|
||||
chocolatey_version = chocolatey.chocolatey_version(refresh=True)
|
||||
except CommandExecutionError:
|
||||
chocolatey_version = None
|
||||
assert chocolatey_version == "1.4.0"
|
128
tests/pytests/functional/states/chocolatey/test_bootstrap.py
Normal file
128
tests/pytests/functional/states/chocolatey/test_bootstrap.py
Normal file
|
@ -0,0 +1,128 @@
|
|||
import pytest
|
||||
|
||||
from salt.exceptions import CommandExecutionError
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.destructive_test,
|
||||
pytest.mark.skip_unless_on_windows,
|
||||
pytest.mark.slow_test,
|
||||
pytest.mark.windows_whitelisted,
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def chocolatey(states):
|
||||
yield states.chocolatey
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def chocolatey_mod(modules):
|
||||
yield modules.chocolatey
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def clean(chocolatey_mod):
|
||||
try:
|
||||
# If chocolatey is not installed, this will throw an error
|
||||
chocolatey_mod.chocolatey_version(refresh=True)
|
||||
# If we get this far, chocolatey is installed... let's uninstall
|
||||
chocolatey_mod.unbootstrap()
|
||||
except CommandExecutionError:
|
||||
pass
|
||||
|
||||
# Try to get the new version, should throw an error
|
||||
try:
|
||||
chocolatey_version = chocolatey_mod.chocolatey_version(refresh=True)
|
||||
except CommandExecutionError:
|
||||
chocolatey_version = None
|
||||
|
||||
# Assert the chocolatey is not installed
|
||||
assert chocolatey_version is None
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
try:
|
||||
# If chocolatey is not installed, this will throw an error
|
||||
chocolatey_mod.chocolatey_version(refresh=True)
|
||||
# If we get this far, chocolatey is installed... let's uninstall
|
||||
chocolatey_mod.unbootstrap()
|
||||
except CommandExecutionError:
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def installed(chocolatey_mod):
|
||||
# Even though this variable is not used, it will be displayed in test output
|
||||
# if there is an error
|
||||
result = chocolatey_mod.bootstrap(force=True)
|
||||
try:
|
||||
chocolatey_version = chocolatey_mod.chocolatey_version(refresh=True)
|
||||
except CommandExecutionError:
|
||||
chocolatey_version = None
|
||||
assert chocolatey_version is not None
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
try:
|
||||
# If chocolatey is not installed, this will throw an error
|
||||
chocolatey_mod.chocolatey_version(refresh=True)
|
||||
# If we get this far, chocolatey is installed... let's uninstall
|
||||
chocolatey_mod.unbootstrap()
|
||||
except CommandExecutionError:
|
||||
pass
|
||||
|
||||
|
||||
def test_bootstrapped(chocolatey, chocolatey_mod, clean):
|
||||
ret = chocolatey.bootstrapped(name="junk name")
|
||||
assert "Installed chocolatey" in ret.comment
|
||||
assert ret.result is True
|
||||
try:
|
||||
chocolatey_version = chocolatey_mod.chocolatey_version(refresh=True)
|
||||
except CommandExecutionError:
|
||||
chocolatey_version = None
|
||||
assert chocolatey_version is not None
|
||||
|
||||
|
||||
def test_bootstrapped_test_true(chocolatey, clean):
|
||||
ret = chocolatey.bootstrapped(name="junk name", test=True)
|
||||
assert ret.result is None
|
||||
assert ret.comment == "The latest version of Chocolatey will be installed"
|
||||
|
||||
|
||||
def test_bootstrapped_version(chocolatey, chocolatey_mod, clean):
|
||||
ret = chocolatey.bootstrapped(name="junk_name", version="1.4.0")
|
||||
assert ret.comment == "Installed chocolatey 1.4.0"
|
||||
assert ret.result is True
|
||||
try:
|
||||
chocolatey_version = chocolatey_mod.chocolatey_version(refresh=True)
|
||||
except CommandExecutionError:
|
||||
chocolatey_version = None
|
||||
assert chocolatey_version == "1.4.0"
|
||||
|
||||
|
||||
def test_bootstrapped_version_test_true(chocolatey, chocolatey_mod, clean):
|
||||
ret = chocolatey.bootstrapped(name="junk_name", version="1.4.0", test=True)
|
||||
assert ret.comment == "Chocolatey 1.4.0 will be installed"
|
||||
|
||||
|
||||
def test_unbootstrapped_installed(chocolatey, chocolatey_mod, installed):
|
||||
ret = chocolatey.unbootstrapped(name="junk_name")
|
||||
assert "Uninstalled chocolatey" in ret.comment
|
||||
assert ret.result is True
|
||||
try:
|
||||
chocolatey_version = chocolatey_mod.chocolatey_version(refresh=True)
|
||||
except CommandExecutionError:
|
||||
chocolatey_version = None
|
||||
assert chocolatey_version is None
|
||||
|
||||
|
||||
def test_unbootstrapped_installed_test_true(chocolatey, chocolatey_mod, installed):
|
||||
ret = chocolatey.unbootstrapped(name="junk_name", test=True)
|
||||
assert "will be removed" in ret.comment
|
||||
assert ret.result is None
|
||||
|
||||
|
||||
def test_unbootstrapped_clean(chocolatey, chocolatey_mod, clean):
|
||||
ret = chocolatey.unbootstrapped(name="junk_name")
|
||||
assert ret.comment == "Chocolatey not found on this system"
|
||||
assert ret.result is True
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
Functional tests for chocolatey state
|
||||
Functional tests for chocolatey state with Chocolatey 2.0+
|
||||
"""
|
||||
|
||||
import os
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
Functional tests for chocolatey state
|
||||
Functional tests for chocolatey state with Chocolatey < 2.0
|
||||
"""
|
||||
|
||||
import os
|
|
@ -21,13 +21,6 @@ from tests.support.helpers import (
|
|||
patched_environ,
|
||||
)
|
||||
|
||||
try:
|
||||
import pwd
|
||||
|
||||
HAS_PWD = True
|
||||
except ImportError:
|
||||
HAS_PWD = False
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
pytestmark = [
|
||||
|
@ -38,7 +31,7 @@ pytestmark = [
|
|||
def _win_user_where(username, password, program):
|
||||
cmd = f"cmd.exe /c where {program}"
|
||||
ret = salt.utils.win_runas.runas(cmd, username, password)
|
||||
assert ret["retcode"] == 0, "{} returned {}".format(cmd, ret["retcode"])
|
||||
assert ret["retcode"] == 0
|
||||
return ret["stdout"].strip().split("\n")[-1].strip()
|
||||
|
||||
|
||||
|
@ -53,6 +46,12 @@ def venv(tmp_path):
|
|||
yield venv
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def account():
|
||||
with pytest.helpers.create_account(username="pip-account") as _account:
|
||||
yield _account
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def create_virtualenv(modules):
|
||||
def run_command(path, **kwargs):
|
||||
|
@ -135,12 +134,11 @@ def test_pip_installed_errors(tmp_path, modules, state_tree):
|
|||
# * "Error installing 'pep8': /bin/sh: 1: /tmp/pip-installed-errors: not found"
|
||||
# * "Error installing 'pep8': /bin/bash: /tmp/pip-installed-errors: No such file or directory"
|
||||
sls_contents = f"""
|
||||
pep8-pip:
|
||||
pip.installed:
|
||||
- name: pep8
|
||||
- bin_env: '{venv_dir}'
|
||||
"""
|
||||
|
||||
pep8-pip:
|
||||
pip.installed:
|
||||
- name: pep8
|
||||
- bin_env: '{venv_dir}'
|
||||
"""
|
||||
with patched_environ(SHELL="/bin/sh"):
|
||||
with pytest.helpers.temp_file(
|
||||
"pip-installed-errors.sls", sls_contents, state_tree
|
||||
|
@ -186,33 +184,33 @@ def test_issue_2028_pip_installed_state(
|
|||
venv_dir = tmp_path / "issue-2028-pip-installed"
|
||||
|
||||
sls_contents = """
|
||||
{%- set virtualenv_base = salt['pillar.get']('venv_dir') %}
|
||||
{%- set python_executable = salt['pillar.get']('python_executable') %}
|
||||
{%- set virtualenv_base = salt['pillar.get']('venv_dir') %}
|
||||
{%- set python_executable = salt['pillar.get']('python_executable') %}
|
||||
|
||||
{{ virtualenv_base }}:
|
||||
virtualenv.managed:
|
||||
- system_site_packages: False
|
||||
- distribute: False
|
||||
{#- Provide the real path for the python executable in case tests are running inside a virtualenv #}
|
||||
{%- if python_executable %}
|
||||
- python: {{ python_executable }}
|
||||
{%- endif %}
|
||||
{{ virtualenv_base }}:
|
||||
virtualenv.managed:
|
||||
- system_site_packages: False
|
||||
- distribute: False
|
||||
{#- Provide the real path for the python executable in case tests are running inside a virtualenv #}
|
||||
{%- if python_executable %}
|
||||
- python: {{ python_executable }}
|
||||
{%- endif %}
|
||||
|
||||
install-working-setuptools:
|
||||
pip.installed:
|
||||
- name: 'setuptools!=50.*,!=51.*,!=52.*'
|
||||
- bin_env: {{ virtualenv_base }}
|
||||
- require:
|
||||
- virtualenv: {{ virtualenv_base }}
|
||||
install-working-setuptools:
|
||||
pip.installed:
|
||||
- name: 'setuptools!=50.*,!=51.*,!=52.*'
|
||||
- bin_env: {{ virtualenv_base }}
|
||||
- require:
|
||||
- virtualenv: {{ virtualenv_base }}
|
||||
|
||||
pep8-pip:
|
||||
pip.installed:
|
||||
- name: pep8
|
||||
- bin_env: {{ virtualenv_base }}
|
||||
- require:
|
||||
- pip: install-working-setuptools
|
||||
- virtualenv: {{ virtualenv_base }}
|
||||
"""
|
||||
pep8-pip:
|
||||
pip.installed:
|
||||
- name: pep8
|
||||
- bin_env: {{ virtualenv_base }}
|
||||
- require:
|
||||
- pip: install-working-setuptools
|
||||
- virtualenv: {{ virtualenv_base }}
|
||||
"""
|
||||
with pytest.helpers.temp_file(
|
||||
"issue-2028-pip-installed.sls", sls_contents, state_tree
|
||||
):
|
||||
|
@ -278,64 +276,56 @@ def test_issue_2087_missing_pip(modules, venv, pkg_name, state_tree):
|
|||
@pytest.mark.destructive_test
|
||||
@pytest.mark.slow_test
|
||||
@pytest.mark.skip_if_not_root
|
||||
def test_issue_6912_wrong_owner(tmp_path, create_virtualenv, modules, states):
|
||||
def test_issue_6912_wrong_owner(tmp_path, create_virtualenv, states, account):
|
||||
# Setup virtual environment directory to be used throughout the test
|
||||
venv_dir = tmp_path / "6912-wrong-owner"
|
||||
venv_kwargs = {}
|
||||
|
||||
with pytest.helpers.create_account(
|
||||
username="issue-6912", password="PassWord1!"
|
||||
) as account:
|
||||
# The virtual environment needs to be in a location that is accessible
|
||||
# by both the user running the test and the runas user
|
||||
if salt.utils.platform.is_windows():
|
||||
salt.utils.win_dacl.set_permissions(
|
||||
tmp_path, account.username, "full_control"
|
||||
)
|
||||
# Make sure we're calling a virtualenv and python
|
||||
# program that the user has access too.
|
||||
venv_kwargs["venv_bin"] = _win_user_where(
|
||||
account.username,
|
||||
"PassWord1!",
|
||||
"virtualenv",
|
||||
)
|
||||
venv_kwargs["python"] = _win_user_where(
|
||||
account.username,
|
||||
"PassWord1!",
|
||||
"python",
|
||||
)
|
||||
else:
|
||||
uid = modules.file.user_to_uid(account.username)
|
||||
os.chown(str(tmp_path), uid, -1)
|
||||
|
||||
# Create the virtual environment
|
||||
venv_create = create_virtualenv(
|
||||
str(venv_dir), user=account.username, password="PassWord1!", **venv_kwargs
|
||||
# The virtual environment needs to be in a location that is accessible
|
||||
# by both the user running the test and the runas user
|
||||
if salt.utils.platform.is_windows():
|
||||
salt.utils.win_dacl.set_permissions(tmp_path, account.username, "full_control")
|
||||
# Make sure we're calling a virtualenv and python
|
||||
# program that the user has access too.
|
||||
venv_kwargs["venv_bin"] = _win_user_where(
|
||||
account.username,
|
||||
account.password,
|
||||
"virtualenv",
|
||||
)
|
||||
if venv_create.get("retcode", 1) > 0:
|
||||
pytest.skip(f"Failed to create testcase virtual environment: {venv_create}")
|
||||
|
||||
# pip install passing the package name in `name`
|
||||
ret = states.pip.installed(
|
||||
name="pep8",
|
||||
user=account.username,
|
||||
bin_env=str(venv_dir),
|
||||
password="PassWord1!",
|
||||
venv_kwargs["python"] = _win_user_where(
|
||||
account.username,
|
||||
account.password,
|
||||
"python",
|
||||
)
|
||||
assert ret.result is True
|
||||
else:
|
||||
os.chown(str(tmp_path), account.info.uid, -1)
|
||||
|
||||
if HAS_PWD:
|
||||
uid = pwd.getpwnam(account.username).pw_uid
|
||||
for globmatch in (
|
||||
os.path.join(str(venv_dir), "**", "pep8*"),
|
||||
os.path.join(str(venv_dir), "*", "**", "pep8*"),
|
||||
os.path.join(str(venv_dir), "*", "*", "**", "pep8*"),
|
||||
):
|
||||
for path in glob.glob(globmatch):
|
||||
if HAS_PWD:
|
||||
assert uid == os.stat(path).st_uid
|
||||
elif salt.utils.platform.is_windows():
|
||||
assert salt.utils.win_dacl.get_owner(path) == account.username
|
||||
# Create the virtual environment
|
||||
venv_create = create_virtualenv(
|
||||
str(venv_dir), user=account.username, password=account.password, **venv_kwargs
|
||||
)
|
||||
if venv_create.get("retcode", 1) > 0:
|
||||
pytest.skip(f"Failed to create testcase virtual environment: {venv_create}")
|
||||
|
||||
# pip install passing the package name in `name`
|
||||
ret = states.pip.installed(
|
||||
name="pep8",
|
||||
user=account.username,
|
||||
password=account.password,
|
||||
bin_env=str(venv_dir),
|
||||
)
|
||||
assert ret.result is True
|
||||
|
||||
for globmatch in (
|
||||
os.path.join(str(venv_dir), "**", "pep8*"),
|
||||
os.path.join(str(venv_dir), "*", "**", "pep8*"),
|
||||
os.path.join(str(venv_dir), "*", "*", "**", "pep8*"),
|
||||
):
|
||||
for path in glob.glob(globmatch):
|
||||
if salt.utils.platform.is_windows():
|
||||
assert salt.utils.win_dacl.get_owner(path) == account.username
|
||||
else:
|
||||
assert os.stat(path).st_uid == account.info.uid
|
||||
|
||||
|
||||
@SKIP_INITIAL_PHOTONOS_FAILURES
|
||||
|
@ -344,70 +334,62 @@ def test_issue_6912_wrong_owner(tmp_path, create_virtualenv, modules, states):
|
|||
@pytest.mark.slow_test
|
||||
@pytest.mark.skip_if_not_root
|
||||
def test_issue_6912_wrong_owner_requirements_file(
|
||||
tmp_path, create_virtualenv, state_tree, modules, states
|
||||
tmp_path, create_virtualenv, state_tree, states, account
|
||||
):
|
||||
# Setup virtual environment directory to be used throughout the test
|
||||
venv_dir = tmp_path / "6912-wrong-owner"
|
||||
venv_kwargs = {}
|
||||
|
||||
with pytest.helpers.create_account(
|
||||
username="issue-6912", password="PassWord1!"
|
||||
) as account:
|
||||
# The virtual environment needs to be in a location that is accessible
|
||||
# by both the user running the test and the runas user
|
||||
if salt.utils.platform.is_windows():
|
||||
salt.utils.win_dacl.set_permissions(
|
||||
str(tmp_path), account.username, "full_control"
|
||||
)
|
||||
# Make sure we're calling a virtualenv and python
|
||||
# program that the user has access too.
|
||||
venv_kwargs["venv_bin"] = _win_user_where(
|
||||
account.username,
|
||||
"PassWord1!",
|
||||
"virtualenv",
|
||||
)
|
||||
venv_kwargs["python"] = _win_user_where(
|
||||
account.username,
|
||||
"PassWord1!",
|
||||
"python",
|
||||
)
|
||||
else:
|
||||
uid = modules.file.user_to_uid(account.username)
|
||||
os.chown(str(tmp_path), uid, -1)
|
||||
|
||||
# Create the virtual environment again as it should have been removed
|
||||
venv_create = create_virtualenv(
|
||||
str(venv_dir), user=account.username, password="PassWord1!", **venv_kwargs
|
||||
# The virtual environment needs to be in a location that is accessible
|
||||
# by both the user running the test and the runas user
|
||||
if salt.utils.platform.is_windows():
|
||||
salt.utils.win_dacl.set_permissions(
|
||||
str(tmp_path), account.username, "full_control"
|
||||
)
|
||||
if venv_create.get("retcode", 1) > 0:
|
||||
pytest.skip(f"failed to create testcase virtual environment: {venv_create}")
|
||||
# Make sure we're calling a virtualenv and python
|
||||
# program that the user has access too.
|
||||
venv_kwargs["venv_bin"] = _win_user_where(
|
||||
account.username,
|
||||
account.password,
|
||||
"virtualenv",
|
||||
)
|
||||
venv_kwargs["python"] = _win_user_where(
|
||||
account.username,
|
||||
account.password,
|
||||
"python",
|
||||
)
|
||||
else:
|
||||
os.chown(str(tmp_path), account.info.uid, -1)
|
||||
|
||||
# pip install using a requirements file
|
||||
contents = "pep8\n"
|
||||
with pytest.helpers.temp_file(
|
||||
"issue-6912-requirements.txt", contents, state_tree
|
||||
):
|
||||
ret = states.pip.installed(
|
||||
name="",
|
||||
user=account.username,
|
||||
bin_env=str(venv_dir),
|
||||
requirements="salt://issue-6912-requirements.txt",
|
||||
password="PassWord1!",
|
||||
)
|
||||
assert ret.result is True
|
||||
# Create the virtual environment again as it should have been removed
|
||||
venv_create = create_virtualenv(
|
||||
str(venv_dir), user=account.username, password=account.password, **venv_kwargs
|
||||
)
|
||||
if venv_create.get("retcode", 1) > 0:
|
||||
pytest.skip(f"failed to create testcase virtual environment: {venv_create}")
|
||||
|
||||
if HAS_PWD:
|
||||
uid = pwd.getpwnam(account.username).pw_uid
|
||||
for globmatch in (
|
||||
os.path.join(str(venv_dir), "**", "pep8*"),
|
||||
os.path.join(str(venv_dir), "*", "**", "pep8*"),
|
||||
os.path.join(str(venv_dir), "*", "*", "**", "pep8*"),
|
||||
):
|
||||
for path in glob.glob(globmatch):
|
||||
if HAS_PWD:
|
||||
assert uid == os.stat(path).st_uid
|
||||
elif salt.utils.platform.is_windows():
|
||||
assert salt.utils.win_dacl.get_owner(path) == account.username
|
||||
# pip install using a requirements file
|
||||
contents = "pep8\n"
|
||||
with pytest.helpers.temp_file("issue-6912-requirements.txt", contents, state_tree):
|
||||
ret = states.pip.installed(
|
||||
name="",
|
||||
user=account.username,
|
||||
password=account.password,
|
||||
bin_env=str(venv_dir),
|
||||
requirements="salt://issue-6912-requirements.txt",
|
||||
)
|
||||
assert ret.result is True
|
||||
|
||||
for globmatch in (
|
||||
os.path.join(str(venv_dir), "**", "pep8*"),
|
||||
os.path.join(str(venv_dir), "*", "**", "pep8*"),
|
||||
os.path.join(str(venv_dir), "*", "*", "**", "pep8*"),
|
||||
):
|
||||
for path in glob.glob(globmatch):
|
||||
if salt.utils.platform.is_windows():
|
||||
assert salt.utils.win_dacl.get_owner(path) == account.username
|
||||
else:
|
||||
assert os.stat(path).st_uid == account.info.uid
|
||||
|
||||
|
||||
@pytest.mark.destructive_test
|
||||
|
|
0
tests/pytests/functional/utils/gitfs/__init__.py
Normal file
0
tests/pytests/functional/utils/gitfs/__init__.py
Normal file
|
@ -95,7 +95,7 @@ def salt_run_cli(salt_master):
|
|||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def salt_ssh_cli(salt_master, salt_ssh_roster_file, sshd_config_dir):
|
||||
def salt_ssh_cli(salt_master, salt_ssh_roster_file, sshd_config_dir, known_hosts_file):
|
||||
"""
|
||||
The ``salt-ssh`` CLI as a fixture against the running master
|
||||
"""
|
||||
|
@ -105,7 +105,6 @@ def salt_ssh_cli(salt_master, salt_ssh_roster_file, sshd_config_dir):
|
|||
roster_file=salt_ssh_roster_file,
|
||||
target_host="localhost",
|
||||
client_key=str(sshd_config_dir / "client_key"),
|
||||
base_script_args=["--ignore-host-keys"],
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -192,7 +192,6 @@ def test_shell_inject_tgt(client, salt_ssh_roster_file, tmp_path, salt_auto_acco
|
|||
"eauth": "auto",
|
||||
"username": salt_auto_account.username,
|
||||
"password": salt_auto_account.password,
|
||||
"ignore_host_keys": True,
|
||||
}
|
||||
ret = client.run(low)
|
||||
assert path.exists() is False
|
||||
|
|
|
@ -70,26 +70,23 @@ def ssh_port(ssh_docker_container):
|
|||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def salt_ssh_roster_file(ssh_port, ssh_keys, salt_master, ssh_auth):
|
||||
def salt_ssh_roster_file(ssh_port, ssh_keys, salt_master, ssh_auth, known_hosts_file):
|
||||
"""
|
||||
Temporary roster for ssh docker container
|
||||
"""
|
||||
ssh_pass, ssh_user = ssh_auth
|
||||
roster = """
|
||||
roster = f"""
|
||||
pyvertest:
|
||||
host: localhost
|
||||
user: {}
|
||||
port: {}
|
||||
passwd: {}
|
||||
user: {ssh_user}
|
||||
port: {ssh_port}
|
||||
passwd: {ssh_pass}
|
||||
sudo: True
|
||||
sudo_user: root
|
||||
tty: True
|
||||
ssh_options:
|
||||
- StrictHostKeyChecking=no
|
||||
- UserKnownHostsFile=/dev/null
|
||||
""".format(
|
||||
ssh_user, ssh_port, ssh_pass
|
||||
)
|
||||
- UserKnownHostsFile={known_hosts_file}
|
||||
"""
|
||||
with pytest.helpers.temp_file(
|
||||
"py_versions_roster", roster, salt_master.config_dir
|
||||
) as roster_file:
|
||||
|
@ -104,8 +101,8 @@ def salt_ssh_cli(salt_master, salt_ssh_roster_file, ssh_keys, ssh_docker_contain
|
|||
timeout=180,
|
||||
roster_file=salt_ssh_roster_file,
|
||||
target_host="localhost",
|
||||
base_script_args=["--ignore-host-keys"],
|
||||
ssh_user="app-admin",
|
||||
base_script_args=["--ignore-host-keys"],
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -251,7 +251,7 @@ def _assert_saltutil_runner_pillar(ret, salt_minion_id):
|
|||
|
||||
@pytest.mark.skip_if_binaries_missing("gpg")
|
||||
@pytest.mark.usefixtures("pillar_setup", "gpg_homedir")
|
||||
def test_gpg_pillar_orch(salt_ssh_cli, salt_run_cli, gpg_homedir):
|
||||
def test_gpg_pillar_orch(salt_ssh_cli, salt_run_cli):
|
||||
"""
|
||||
Ensure that GPG-encrypted pillars can be decrypted when Salt-SSH is
|
||||
called during an orchestration or via saltutil.cmd.
|
||||
|
@ -265,7 +265,6 @@ def test_gpg_pillar_orch(salt_ssh_cli, salt_run_cli, gpg_homedir):
|
|||
salt_ssh_cli.target_host,
|
||||
"pillar.items",
|
||||
ssh=True,
|
||||
ignore_host_keys=True,
|
||||
roster_file=str(salt_ssh_cli.roster_file),
|
||||
ssh_priv=str(salt_ssh_cli.client_key),
|
||||
)
|
||||
|
@ -290,7 +289,6 @@ def test_saltutil_runner_orch(salt_ssh_cli, salt_run_cli, salt_minion):
|
|||
salt_ssh_cli.target_host,
|
||||
"pillar.items",
|
||||
ssh=True,
|
||||
ignore_host_keys=True,
|
||||
roster_file=str(salt_ssh_cli.roster_file),
|
||||
ssh_priv=str(salt_ssh_cli.client_key),
|
||||
)
|
||||
|
|
|
@ -85,22 +85,19 @@ def ssh_port(ssh_docker_container):
|
|||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def salt_ssh_roster_file(ssh_port, ssh_keys, salt_master):
|
||||
def salt_ssh_roster_file(ssh_port, ssh_keys, salt_master, known_hosts_file):
|
||||
"""
|
||||
Temporary roster for ssh docker container
|
||||
"""
|
||||
roster = """
|
||||
roster = f"""
|
||||
pyvertest:
|
||||
host: localhost
|
||||
user: centos
|
||||
port: {}
|
||||
priv: {}
|
||||
port: {ssh_port}
|
||||
priv: {ssh_keys.priv_path}
|
||||
ssh_options:
|
||||
- StrictHostKeyChecking=no
|
||||
- UserKnownHostsFile=/dev/null
|
||||
""".format(
|
||||
ssh_port, ssh_keys.priv_path
|
||||
)
|
||||
- UserKnownHostsFile={known_hosts_file}
|
||||
"""
|
||||
with pytest.helpers.temp_file(
|
||||
"py_versions_roster", roster, salt_master.config_dir
|
||||
) as roster_file:
|
||||
|
|
|
@ -117,23 +117,30 @@ def ssh_sub_port(ssh_sub_container):
|
|||
|
||||
@pytest.fixture(scope="module")
|
||||
def salt_ssh_roster_file(
|
||||
ssh_container_name, ssh_sub_container_name, ssh_port, ssh_sub_port, salt_master
|
||||
ssh_container_name,
|
||||
ssh_sub_container_name,
|
||||
ssh_port,
|
||||
ssh_sub_port,
|
||||
salt_master,
|
||||
known_hosts_file,
|
||||
):
|
||||
"""
|
||||
Temporary roster for ssh docker container
|
||||
"""
|
||||
roster = """
|
||||
{}:
|
||||
roster = f"""
|
||||
{ssh_container_name}:
|
||||
host: localhost
|
||||
user: centos
|
||||
port: {}
|
||||
{}:
|
||||
port: {ssh_port}
|
||||
ssh_options:
|
||||
- UserKnownHostsFile={known_hosts_file}
|
||||
{ssh_sub_container_name}:
|
||||
host: localhost
|
||||
user: centos
|
||||
port: {}
|
||||
""".format(
|
||||
ssh_container_name, ssh_port, ssh_sub_container_name, ssh_sub_port
|
||||
)
|
||||
port: {ssh_sub_port}
|
||||
ssh_options:
|
||||
- UserKnownHostsFile={known_hosts_file}
|
||||
"""
|
||||
with pytest.helpers.temp_file(
|
||||
"setup_roster", roster, salt_master.config_dir
|
||||
) as roster_file:
|
||||
|
@ -151,7 +158,6 @@ def salt_ssh_cli(
|
|||
timeout=180,
|
||||
roster_file=salt_ssh_roster_file,
|
||||
client_key=str(ssh_keys.priv_path),
|
||||
base_script_args=["--ignore-host-keys"],
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ def ansible_inventory_directory(tmp_path_factory, grains):
|
|||
|
||||
|
||||
@pytest.fixture(scope="module", autouse=True)
|
||||
def ansible_inventory(ansible_inventory_directory, sshd_server):
|
||||
def ansible_inventory(ansible_inventory_directory, sshd_server, known_hosts_file):
|
||||
inventory = str(ansible_inventory_directory / "inventory")
|
||||
client_key = str(sshd_server.config_dir / "client_key")
|
||||
data = {
|
||||
|
@ -52,8 +52,7 @@ def ansible_inventory(ansible_inventory_directory, sshd_server):
|
|||
"ansible_user": RUNTIME_VARS.RUNNING_TESTS_USER,
|
||||
"ansible_ssh_private_key_file": client_key,
|
||||
"ansible_ssh_extra_args": (
|
||||
"-o StrictHostKeyChecking=false "
|
||||
"-o UserKnownHostsFile=/dev/null "
|
||||
f"-o UserKnownHostsFile={known_hosts_file} "
|
||||
),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -7,14 +7,10 @@ import os
|
|||
import pytest
|
||||
|
||||
import salt.modules.chocolatey as chocolatey
|
||||
import salt.utils
|
||||
import salt.utils.platform
|
||||
from tests.support.mock import MagicMock, patch
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.skipif(
|
||||
not salt.utils.platform.is_windows(), reason="Not a Windows system"
|
||||
)
|
||||
pytest.mark.skip_unless_on_windows,
|
||||
]
|
||||
|
||||
|
||||
|
@ -68,7 +64,7 @@ def test__clear_context(choco_path):
|
|||
}
|
||||
with patch.dict(chocolatey.__context__, context):
|
||||
chocolatey._clear_context()
|
||||
# Did it clear all chocolatey items from __context__P?
|
||||
# Did it clear all chocolatey items from __context__?
|
||||
assert chocolatey.__context__ == {}
|
||||
|
||||
|
||||
|
@ -333,3 +329,25 @@ def test_list_windowsfeatures_post_2_0_0():
|
|||
chocolatey.list_windowsfeatures()
|
||||
expected_call = [choco_path, "search", "--source", "windowsfeatures"]
|
||||
mock_run.assert_called_with(expected_call, python_shell=False)
|
||||
|
||||
|
||||
def test_chocolatey_version():
|
||||
context = {
|
||||
"chocolatey._version": "0.9.9",
|
||||
}
|
||||
with patch.dict(chocolatey.__context__, context):
|
||||
result = chocolatey.chocolatey_version()
|
||||
expected = "0.9.9"
|
||||
assert result == expected
|
||||
|
||||
|
||||
def test_chocolatey_version_refresh():
|
||||
context = {"chocolatey._version": "0.9.9"}
|
||||
mock_find = MagicMock(return_value="some_path")
|
||||
mock_run = MagicMock(return_value="2.2.0")
|
||||
with patch.dict(chocolatey.__context__, context), patch.object(
|
||||
chocolatey, "_find_chocolatey", mock_find
|
||||
), patch.dict(chocolatey.__salt__, {"cmd.run": mock_run}):
|
||||
result = chocolatey.chocolatey_version(refresh=True)
|
||||
expected = "2.2.0"
|
||||
assert result == expected
|
||||
|
|
Loading…
Add table
Reference in a new issue