diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 0f61ba09040..31345a58b6e 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -20,12 +20,14 @@ jobs: github.event.pull_request.merged == true && ( contains(github.event.pull_request.labels.*.name, 'backport:master') || + contains(github.event.pull_request.labels.*.name, 'backport:3007.x') || contains(github.event.pull_request.labels.*.name, 'backport:3006.x') || contains(github.event.pull_request.labels.*.name, 'backport:3005.x') ) && ( (github.event.action == 'labeled' && ( contains(github.event.pull_request.labels.*.name, 'backport:master') || + contains(github.event.pull_request.labels.*.name, 'backport:3007.x') || contains(github.event.pull_request.labels.*.name, 'backport:3006.x') || contains(github.event.pull_request.labels.*.name, 'backport:3005.x') )) diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index adeeb2fff67..a4544923cc2 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -32,7 +32,6 @@ jobs: - linkcheck - spellcheck - html - - epub # - pdf steps: diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 1a1160f7521..3eac8682d00 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -2868,12 +2868,6 @@ jobs: name: salt-${{ needs.prepare-workflow.outputs.salt-version }}-docs-html.tar.xz path: artifacts/release - - name: Download Release Documentation (ePub) - uses: actions/download-artifact@v4 - with: - name: Salt-${{ needs.prepare-workflow.outputs.salt-version }}.epub - path: artifacts/release - - name: Show Release Artifacts run: | tree -a artifacts/release diff --git a/.github/workflows/templates/staging.yml.jinja b/.github/workflows/templates/staging.yml.jinja index ae096e51e35..a15302bc00a 100644 --- a/.github/workflows/templates/staging.yml.jinja +++ b/.github/workflows/templates/staging.yml.jinja @@ -124,12 +124,6 @@ concurrency: name: salt-${{ needs.prepare-workflow.outputs.salt-version }}-docs-html.tar.xz path: artifacts/release - - name: Download Release Documentation (ePub) - uses: actions/download-artifact@v4 - with: - name: Salt-${{ needs.prepare-workflow.outputs.salt-version }}.epub - path: artifacts/release - - name: Show Release Artifacts run: | tree -a artifacts/release diff --git a/.github/workflows/test-action-windows.yml b/.github/workflows/test-action-windows.yml index 4110a3a644d..dc54f501e69 100644 --- a/.github/workflows/test-action-windows.yml +++ b/.github/workflows/test-action-windows.yml @@ -374,8 +374,8 @@ jobs: if: always() && inputs.skip-code-coverage == false && steps.download-coverage-artifacts.outcome == 'success' && job.status != 'cancelled' run: | nox --force-color -e create-xml-coverage-reports - mv artifacts/coverage/salt.xml artifacts/coverage/salt..${{ inputs.distro-slug }}..${{ inputs.nox-session }}.xml - mv artifacts/coverage/tests.xml artifacts/coverage/tests..${{ inputs.distro-slug }}..${{ inputs.nox-session }}.xml + mv artifacts/coverage/salt.xml artifacts/coverage/salt..${{ inputs.distro-slug }}..${{ inputs.nox-session }}.xml || true + mv artifacts/coverage/tests.xml artifacts/coverage/tests..${{ inputs.distro-slug }}..${{ inputs.nox-session }}.xml || true - name: Report Salt Code Coverage if: always() && inputs.skip-code-coverage == false && steps.download-coverage-artifacts.outcome == 'success' diff --git a/.github/workflows/test-installer-action-windows.yml b/.github/workflows/test-installer-action-windows.yml new file mode 100644 index 00000000000..cf0b48556bb --- /dev/null +++ b/.github/workflows/test-installer-action-windows.yml @@ -0,0 +1,38 @@ +--- +name: Test Windows Installer + +on: pull_request + +permissions: + contents: read + +jobs: + Test-Windows-Installer: + runs-on: + - windows-latest + + steps: + + - name: Checkout Salt + uses: actions/checkout@v4 + + - name: Set Up Python 3.10 + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install NSIS + run: .\pkg\windows\install_nsis.cmd -CICD + shell: cmd + + - name: Build Test Installer + run: .\pkg\windows\nsis\tests\setup.cmd -CICD + shell: cmd + + - name: Run Stress Test + run: .\pkg\windows\nsis\tests\test.cmd -CICD .\stress_tests + shell: cmd + + - name: Run Config Tests + run: .\pkg\windows\nsis\tests\test.cmd -CICD .\config_tests + shell: cmd diff --git a/.github/workflows/test-packages-action-linux.yml b/.github/workflows/test-packages-action-linux.yml index 432b8e04bb4..36b4d7818d4 100644 --- a/.github/workflows/test-packages-action-linux.yml +++ b/.github/workflows/test-packages-action-linux.yml @@ -192,12 +192,6 @@ jobs: run: | tools --timestamps vm decompress-dependencies ${{ inputs.distro-slug }} - - name: Downgrade importlib-metadata - if: ${{ contains(fromJSON('["amazonlinux-2", "centos-7"]'), inputs.distro-slug) && contains(fromJSON('["upgrade-classic", "downgrade-classic"]'), matrix.tests-chunk) }} - run: | - # This step can go away once we stop testing classic packages upgrade/downgrades to/from 3005.x - tools --timestamps vm ssh ${{ inputs.distro-slug }} -- "sudo python3 -m pip install -U 'importlib-metadata<=4.13.0' 'virtualenv<=20.21.1'" - - name: Show System Info run: | tools --timestamps --timeout-secs=1800 vm test --skip-requirements-install --print-system-information-only \ diff --git a/.github/workflows/test-packages-action-macos.yml b/.github/workflows/test-packages-action-macos.yml index 5e8c3069178..aca0b4cc244 100644 --- a/.github/workflows/test-packages-action-macos.yml +++ b/.github/workflows/test-packages-action-macos.yml @@ -105,7 +105,7 @@ jobs: test: name: Test runs-on: ${{ inputs.runner }} - timeout-minutes: 120 # 2 Hours - More than this and something is wrong + timeout-minutes: 150 # 2 & 1/2 Hours - More than this and something is wrong (MacOS needs a little more time) needs: - generate-matrix strategy: diff --git a/.github/workflows/test-packages-action-windows.yml b/.github/workflows/test-packages-action-windows.yml index c21100f4e69..985ff96de82 100644 --- a/.github/workflows/test-packages-action-windows.yml +++ b/.github/workflows/test-packages-action-windows.yml @@ -191,12 +191,6 @@ jobs: run: | tools --timestamps vm decompress-dependencies ${{ inputs.distro-slug }} - - name: Downgrade importlib-metadata - if: ${{ contains(fromJSON('["amazonlinux-2", "centos-7"]'), inputs.distro-slug) && contains(fromJSON('["upgrade-classic", "downgrade-classic"]'), matrix.tests-chunk) }} - run: | - # This step can go away once we stop testing classic packages upgrade/downgrades to/from 3005.x - tools --timestamps vm ssh ${{ inputs.distro-slug }} -- "sudo python3 -m pip install -U 'importlib-metadata<=4.13.0' 'virtualenv<=20.21.1'" - - name: Show System Info run: | tools --timestamps --timeout-secs=1800 vm test --skip-requirements-install --print-system-information-only \ diff --git a/CHANGELOG.md b/CHANGELOG.md index a5a5912826d..1a64b4f6e2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,10 +13,23 @@ Versions are `MAJOR.PATCH`. ### Removed - The ``salt.utils.psutil_compat`` was deprecated and now removed in Salt 3008. Please use the ``psutil`` module directly. [#66160](https://github.com/saltstack/salt/issues/66160) +## 3006.9 (2024-07-29) + + +### Deprecated + +- Drop CentOS 7 support [#66623](https://github.com/saltstack/salt/issues/66623) +- No longer build RPM packages with CentOS Stream 9 [#66624](https://github.com/saltstack/salt/issues/66624) ### Fixed +- Made slsutil.renderer work with salt-ssh [#50196](https://github.com/saltstack/salt/issues/50196) +- Fixed defaults.merge is not available when using salt-ssh [#51605](https://github.com/saltstack/salt/issues/51605) +- Fixed config.get does not support merge option with salt-ssh [#56441](https://github.com/saltstack/salt/issues/56441) +- Update to include croniter in pkg requirements [#57649](https://github.com/saltstack/salt/issues/57649) +- Fixed state.test does not work with salt-ssh [#61100](https://github.com/saltstack/salt/issues/61100) +- Made slsutil.findup work with salt-ssh [#61143](https://github.com/saltstack/salt/issues/61143) - Fixes multiple issues with the cmd module on Windows. Scripts are called using the ``-File`` parameter to the ``powershell.exe`` binary. ``CLIXML`` data in stderr is now removed (only applies to encoded commands). Commands can now be @@ -35,6 +48,38 @@ Versions are `MAJOR.PATCH`. - Change log level of successful master cluster key exchange from error to info. [#66266](https://github.com/saltstack/salt/issues/66266) - Made `file.managed` skip download of a remote source if the managed file already exists with the correct hash [#66342](https://github.com/saltstack/salt/issues/66342) - Fixed nftables.build_rule breaks ipv6 rules by using the wrong syntax for source and destination addresses [#66382](https://github.com/saltstack/salt/issues/66382) +- file.replace and file.search work properly with /proc files [#63102](https://github.com/saltstack/salt/issues/63102) +- Fix utf8 handling in 'pass' renderer [#64300](https://github.com/saltstack/salt/issues/64300) +- Fixed incorrect version argument will be ignored for multiple package targets warning when using pkgs argument to yumpkg module. [#64563](https://github.com/saltstack/salt/issues/64563) +- salt-cloud honors root_dir config setting for log_file location and fixes for root_dir locations on windows. [#64728](https://github.com/saltstack/salt/issues/64728) +- Fixed slsutil.update with salt-ssh during template rendering [#65067](https://github.com/saltstack/salt/issues/65067) +- Fix config.items when called on minion [#65251](https://github.com/saltstack/salt/issues/65251) +- Ensure on rpm and deb systems, that user and group for existing Salt, is maintained on upgrade [#65264](https://github.com/saltstack/salt/issues/65264) +- Fix typo in nftables module to ensure unique nft family values [#65295](https://github.com/saltstack/salt/issues/65295) +- pkg.installed state aggregate does not honors requires requisite [#65304](https://github.com/saltstack/salt/issues/65304) +- Added SSH wrapper for logmod [#65630](https://github.com/saltstack/salt/issues/65630) +- Fix for GitFS failure to unlock lock file, and resource cleanup for process SIGTERM [#65816](https://github.com/saltstack/salt/issues/65816) +- Corrected x509_v2 CRL creation `last_update` and `next_update` values when system timezone is not UTC [#65837](https://github.com/saltstack/salt/issues/65837) +- Make sure the root minion process handles SIGUSR1 and emits a traceback like it's child processes [#66095](https://github.com/saltstack/salt/issues/66095) +- Replaced pyvenv with builtin venv for virtualenv_mod [#66132](https://github.com/saltstack/salt/issues/66132) +- Made `file.managed` skip download of a remote source if the managed file already exists with the correct hash [#66342](https://github.com/saltstack/salt/issues/66342) +- Fix win_task ExecutionTimeLimit and result/error code interpretation [#66347](https://github.com/saltstack/salt/issues/66347), [#66441](https://github.com/saltstack/salt/issues/66441) +- Fixed nftables.build_rule breaks ipv6 rules by using the wrong syntax for source and destination addresses [#66382](https://github.com/saltstack/salt/issues/66382) +- Fixed x509_v2 certificate.managed crash for locally signed certificates if the signing policy defines signing_private_key [#66414](https://github.com/saltstack/salt/issues/66414) +- Fixed parallel state execution with Salt-SSH [#66514](https://github.com/saltstack/salt/issues/66514) +- Fix support for FIPS approved encryption and signing algorithms. [#66579](https://github.com/saltstack/salt/issues/66579) +- Fix relative file_roots paths [#66588](https://github.com/saltstack/salt/issues/66588) +- Fixed an issue with cmd.run with requirements when the shell is not the + default [#66596](https://github.com/saltstack/salt/issues/66596) +- Fix RPM package provides [#66604](https://github.com/saltstack/salt/issues/66604) +- Upgrade relAenv to 0.16.1. This release fixes several package installs for salt-pip [#66632](https://github.com/saltstack/salt/issues/66632) +- Upgrade relenv to 0.17.0 (https://github.com/saltstack/relenv/blob/v0.17.0/CHANGELOG.md) [#66663](https://github.com/saltstack/salt/issues/66663) +- Upgrade dependencies due to security issues: + - pymysql>=1.1.1 + - requests>=2.32.0 + - docker>=7.1.0 [#66666](https://github.com/saltstack/salt/issues/66666) +- Corrected missed line in branch 3006.x when backporting from PR 61620 and 65044 [#66683](https://github.com/saltstack/salt/issues/66683) +- Remove debug output from shell scripts for packaging [#66747](https://github.com/saltstack/salt/issues/66747) ### Added @@ -44,12 +89,18 @@ Versions are `MAJOR.PATCH`. unbootstrap chocolatey. [#64722](https://github.com/saltstack/salt/issues/64722) - Add Ubuntu 24.04 support [#66180](https://github.com/saltstack/salt/issues/66180) - Add Fedora 40 support, replacing Fedora 39 [#66300](https://github.com/saltstack/salt/issues/66300) +- Add Ubuntu 24.04 support [#66180](https://github.com/saltstack/salt/issues/66180) +- Add Fedora 40 support, replacing Fedora 39 [#66300](https://github.com/saltstack/salt/issues/66300) +- Build RPM packages with Rocky Linux 9 (instead of CentOS Stream 9) [#66624](https://github.com/saltstack/salt/issues/66624) ### Security - Bump to `pydantic==2.6.4` due to https://github.com/advisories/GHSA-mr82-8j83-vxmv [#66433](https://github.com/saltstack/salt/issues/66433) - Bump to ``jinja2==3.1.4`` due to https://github.com/advisories/GHSA-h75v-3vvj-5mfj [#66488](https://github.com/saltstack/salt/issues/66488) +- Bump to ``jinja2==3.1.4`` due to https://github.com/advisories/GHSA-h75v-3vvj-5mfj [#66488](https://github.com/saltstack/salt/issues/66488) +- CVE-2024-37088 salt-call will fail with exit code 1 if bad pillar data is + encountered. [#66702](https://github.com/saltstack/salt/issues/66702) ## 3006.8 (2024-04-29) diff --git a/changelog/33669.added.md b/changelog/33669.added.md new file mode 100644 index 00000000000..45fe6ead2ba --- /dev/null +++ b/changelog/33669.added.md @@ -0,0 +1,3 @@ +Issue #33669: Fixes an issue with the ``ini_managed`` execution module +where it would always wrap the separator with spaces. Adds a new parameter +named ``no_spaces`` that will not warp the separator with spaces. diff --git a/changelog/50196.fixed.md b/changelog/50196.fixed.md deleted file mode 100644 index 979411a640d..00000000000 --- a/changelog/50196.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Made slsutil.renderer work with salt-ssh diff --git a/changelog/51605.fixed.md b/changelog/51605.fixed.md deleted file mode 100644 index 990b34413d9..00000000000 --- a/changelog/51605.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed defaults.merge is not available when using salt-ssh diff --git a/changelog/56441.fixed.md b/changelog/56441.fixed.md deleted file mode 100644 index 489ad80f770..00000000000 --- a/changelog/56441.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed config.get does not support merge option with salt-ssh diff --git a/changelog/57649.fixed.md b/changelog/57649.fixed.md deleted file mode 100644 index 12d22a0531c..00000000000 --- a/changelog/57649.fixed.md +++ /dev/null @@ -1 +0,0 @@ - Update to include croniter in pkg requirements diff --git a/changelog/61100.fixed.md b/changelog/61100.fixed.md deleted file mode 100644 index d7ac2b6bc3f..00000000000 --- a/changelog/61100.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed state.test does not work with salt-ssh diff --git a/changelog/61143.fixed.md b/changelog/61143.fixed.md deleted file mode 100644 index 08a62c9d8b1..00000000000 --- a/changelog/61143.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Made slsutil.findup work with salt-ssh diff --git a/changelog/64300.fixed.md b/changelog/64300.fixed.md deleted file mode 100644 index 4418db1d04c..00000000000 --- a/changelog/64300.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fix utf8 handling in 'pass' renderer diff --git a/changelog/64563.fixed.md b/changelog/64563.fixed.md deleted file mode 100644 index fadd9721fed..00000000000 --- a/changelog/64563.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed incorrect version argument will be ignored for multiple package targets warning when using pkgs argument to yumpkg module. diff --git a/changelog/64728.fixed.md b/changelog/64728.fixed.md deleted file mode 100644 index afe36f42316..00000000000 --- a/changelog/64728.fixed.md +++ /dev/null @@ -1 +0,0 @@ -salt-cloud honors root_dir config setting for log_file location and fixes for root_dir locations on windows. diff --git a/changelog/65067.fixed.md b/changelog/65067.fixed.md deleted file mode 100644 index d6de87b5bc1..00000000000 --- a/changelog/65067.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed slsutil.update with salt-ssh during template rendering diff --git a/changelog/65104.fixed.md b/changelog/65104.fixed.md new file mode 100644 index 00000000000..020b990b630 --- /dev/null +++ b/changelog/65104.fixed.md @@ -0,0 +1 @@ +The 'profile' outputter does not crash with incorrectly formatted data diff --git a/changelog/65251.fixed.md b/changelog/65251.fixed.md deleted file mode 100644 index e8abd5af327..00000000000 --- a/changelog/65251.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fix config.items when called on minion diff --git a/changelog/65304.fixed.md b/changelog/65304.fixed.md deleted file mode 100644 index dd162cee714..00000000000 --- a/changelog/65304.fixed.md +++ /dev/null @@ -1 +0,0 @@ -pkg.installed state aggregate does not honors requires requisite diff --git a/changelog/65630.fixed.md b/changelog/65630.fixed.md deleted file mode 100644 index e8650abcdc1..00000000000 --- a/changelog/65630.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Added SSH wrapper for logmod diff --git a/changelog/65816.fixed.md b/changelog/65816.fixed.md deleted file mode 100644 index 23aaa1e5e8e..00000000000 --- a/changelog/65816.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fix for GitFS failure to unlock lock file, and resource cleanup for process SIGTERM diff --git a/changelog/66249.fixed.md b/changelog/66249.fixed.md new file mode 100644 index 00000000000..dac7b563a49 --- /dev/null +++ b/changelog/66249.fixed.md @@ -0,0 +1 @@ +Fix batch mode hang indefinitely in some scenarios diff --git a/changelog/66347.fixed.md b/changelog/66347.fixed.md deleted file mode 100644 index e61e5ce64a9..00000000000 --- a/changelog/66347.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fix win_task ExecutionTimeLimit and result/error code interpretation diff --git a/changelog/66414.fixed.md b/changelog/66414.fixed.md deleted file mode 100644 index e777d18226d..00000000000 --- a/changelog/66414.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed x509_v2 certificate.managed crash for locally signed certificates if the signing policy defines signing_private_key diff --git a/changelog/66441.fixed.md b/changelog/66441.fixed.md deleted file mode 100644 index e61e5ce64a9..00000000000 --- a/changelog/66441.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fix win_task ExecutionTimeLimit and result/error code interpretation diff --git a/changelog/66467.removed.md b/changelog/66467.removed.md new file mode 100644 index 00000000000..aca1198858e --- /dev/null +++ b/changelog/66467.removed.md @@ -0,0 +1 @@ +Remove psutil_compat.py file, which should have been removed when RHEL 6 EOL diff --git a/changelog/66579.fixed.md b/changelog/66579.fixed.md deleted file mode 100644 index ccef663b846..00000000000 --- a/changelog/66579.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fix support for FIPS approved encryption and signing algorithms. diff --git a/changelog/66604.fixed.md b/changelog/66604.fixed.md deleted file mode 100644 index 4d1a771ca54..00000000000 --- a/changelog/66604.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fix RPM package provides diff --git a/changelog/66623.deprecated.md b/changelog/66623.deprecated.md deleted file mode 100644 index 8d829eadec9..00000000000 --- a/changelog/66623.deprecated.md +++ /dev/null @@ -1 +0,0 @@ -Drop CentOS 7 support diff --git a/changelog/66624.added.md b/changelog/66624.added.md deleted file mode 100644 index fbc4adf84c7..00000000000 --- a/changelog/66624.added.md +++ /dev/null @@ -1 +0,0 @@ -Build RPM packages with Rocky Linux 9 (instead of CentOS Stream 9) diff --git a/changelog/66624.deprecated.md b/changelog/66624.deprecated.md deleted file mode 100644 index 10b397bae85..00000000000 --- a/changelog/66624.deprecated.md +++ /dev/null @@ -1 +0,0 @@ -No longer build RPM packages with CentOS Stream 9 diff --git a/changelog/66632.fixed.md b/changelog/66632.fixed.md deleted file mode 100644 index c50213867ca..00000000000 --- a/changelog/66632.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Upgrade relAenv to 0.16.1. This release fixes several package installs for salt-pip diff --git a/changelog/66663.fixed.md b/changelog/66663.fixed.md deleted file mode 100644 index 14a40b4730e..00000000000 --- a/changelog/66663.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Upgrade relenv to 0.17.0 (https://github.com/saltstack/relenv/blob/v0.17.0/CHANGELOG.md) diff --git a/changelog/66666.fixed.md b/changelog/66666.fixed.md deleted file mode 100644 index 076088f4d0c..00000000000 --- a/changelog/66666.fixed.md +++ /dev/null @@ -1,4 +0,0 @@ -Upgrade dependencies due to security issues: -- pymysql>=1.1.1 -- requests>=2.32.0 -- docker>=7.1.0 diff --git a/changelog/66683.fixed.md b/changelog/66683.fixed.md deleted file mode 100644 index 2917188fa63..00000000000 --- a/changelog/66683.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Corrected missed line in branch 3006.x when backporting from PR 61620 and 65044 diff --git a/changelog/66716.fixed.md b/changelog/66716.fixed.md new file mode 100644 index 00000000000..f3ad42f8edf --- /dev/null +++ b/changelog/66716.fixed.md @@ -0,0 +1,2 @@ +Fixed an issue where ``status.master`` wasn't detecting a connection to the +specified master properly diff --git a/changelog/66718.fixed.md b/changelog/66718.fixed.md new file mode 100644 index 00000000000..8a4a15ebad4 --- /dev/null +++ b/changelog/66718.fixed.md @@ -0,0 +1,2 @@ +Fixed ``win_wua.available`` when some of the update objects are empty CDispatch +objects. The ``available`` function no longer crashes diff --git a/changelog/66726.fixed.md b/changelog/66726.fixed.md new file mode 100644 index 00000000000..b9682900d1d --- /dev/null +++ b/changelog/66726.fixed.md @@ -0,0 +1 @@ +Clean up multiprocessing file handles on minion diff --git a/changelog/66786.fixed.md b/changelog/66786.fixed.md new file mode 100644 index 00000000000..22bb47e0806 --- /dev/null +++ b/changelog/66786.fixed.md @@ -0,0 +1,2 @@ +Fix an issue where files created using `salt.utils.atomicile.atomic_open()` +were created with restrictive permissions instead of respecting the umask. diff --git a/changelog/66789.fixed.md b/changelog/66789.fixed.md new file mode 100644 index 00000000000..f6d18c6247d --- /dev/null +++ b/changelog/66789.fixed.md @@ -0,0 +1 @@ +Fix bad async_method name on AsyncPubClient class diff --git a/changelog/66796.fixed.md b/changelog/66796.fixed.md new file mode 100644 index 00000000000..cff6c771fa9 --- /dev/null +++ b/changelog/66796.fixed.md @@ -0,0 +1 @@ +Ensure Manjaro ARM reports the correct os_family of Arch. diff --git a/doc/Makefile b/doc/Makefile index 9b1b1939a9b..60d95e24e81 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -16,7 +16,7 @@ ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -.PHONY: help clean check_sphinx-build html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest +.PHONY: help clean check_sphinx-build html dirhtml singlehtml pickle json htmlhelp qthelp devhelp latex latexpdf text man changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @@ -28,7 +28,6 @@ help: @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " pdf to make Salt-all.pdf and splitted pdf using xelatex" @echo " cheatsheet to create salt-cheatsheet.pdf" @@ -101,11 +100,6 @@ devhelp: check_sphinx-build @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Salt" @echo "# devhelp" -epub: check_sphinx-build - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - latex: check_sphinx-build $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo diff --git a/doc/_themes/saltstack2/layout.html b/doc/_themes/saltstack2/layout.html index 32fff3e6095..f98ae192d7c 100644 --- a/doc/_themes/saltstack2/layout.html +++ b/doc/_themes/saltstack2/layout.html @@ -193,10 +193,8 @@ {% if not (build_type == repo_primary_branch or build_type == "next") and on_saltstack %}
  • -
  • {% elif build_type == repo_primary_branch and on_saltstack %}
  • -
  • {% endif %} diff --git a/doc/_themes/saltstack2/static/images/epub_icon.svg b/doc/_themes/saltstack2/static/images/epub_icon.svg deleted file mode 100644 index e50861b8d3c..00000000000 --- a/doc/_themes/saltstack2/static/images/epub_icon.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - diff --git a/doc/conf.py b/doc/conf.py index 235a6967f50..16deb36b700 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -383,19 +383,6 @@ man_pages = [ ] -### epub options -epub_title = "Salt Documentation" -epub_author = "VMware, Inc." -epub_publisher = epub_author -epub_copyright = copyright - -epub_scheme = "URL" -epub_identifier = "http://saltproject.io/" - -epub_tocdup = False -# epub_tocdepth = 3 - - def skip_mod_init_member(app, what, name, obj, skip, options): # pylint: disable=too-many-arguments,unused-argument if name.startswith("_"): diff --git a/doc/topics/releases/3006.9.md b/doc/topics/releases/3006.9.md new file mode 100644 index 00000000000..5f5d64051d5 --- /dev/null +++ b/doc/topics/releases/3006.9.md @@ -0,0 +1,87 @@ +(release-3006.9)= +# Salt 3006.9 release notes + + + + + + + +## Changelog + +### Deprecated + +- Drop CentOS 7 support [#66623](https://github.com/saltstack/salt/issues/66623) +- No longer build RPM packages with CentOS Stream 9 [#66624](https://github.com/saltstack/salt/issues/66624) + + +### Fixed + +- Made slsutil.renderer work with salt-ssh [#50196](https://github.com/saltstack/salt/issues/50196) +- Fixed defaults.merge is not available when using salt-ssh [#51605](https://github.com/saltstack/salt/issues/51605) +- Fixed config.get does not support merge option with salt-ssh [#56441](https://github.com/saltstack/salt/issues/56441) +- Update to include croniter in pkg requirements [#57649](https://github.com/saltstack/salt/issues/57649) +- Fixed state.test does not work with salt-ssh [#61100](https://github.com/saltstack/salt/issues/61100) +- Made slsutil.findup work with salt-ssh [#61143](https://github.com/saltstack/salt/issues/61143) +- Fixes multiple issues with the cmd module on Windows. Scripts are called using + the ``-File`` parameter to the ``powershell.exe`` binary. ``CLIXML`` data in + stderr is now removed (only applies to encoded commands). Commands can now be + sent to ``cmd.powershell`` as a list. Makes sure JSON data returned is valid. + Strips whitespace from the return when using ``runas``. [#61166](https://github.com/saltstack/salt/issues/61166) +- Fixed the win_lgpo_netsh salt util to handle non-English systems. This was a + rewrite to use PowerShell instead of netsh to make the changes on the system [#61534](https://github.com/saltstack/salt/issues/61534) +- file.replace and file.search work properly with /proc files [#63102](https://github.com/saltstack/salt/issues/63102) +- Fix utf8 handling in 'pass' renderer [#64300](https://github.com/saltstack/salt/issues/64300) +- Fixed incorrect version argument will be ignored for multiple package targets warning when using pkgs argument to yumpkg module. [#64563](https://github.com/saltstack/salt/issues/64563) +- salt-cloud honors root_dir config setting for log_file location and fixes for root_dir locations on windows. [#64728](https://github.com/saltstack/salt/issues/64728) +- Fixed slsutil.update with salt-ssh during template rendering [#65067](https://github.com/saltstack/salt/issues/65067) +- Fix config.items when called on minion [#65251](https://github.com/saltstack/salt/issues/65251) +- Ensure on rpm and deb systems, that user and group for existing Salt, is maintained on upgrade [#65264](https://github.com/saltstack/salt/issues/65264) +- Fix typo in nftables module to ensure unique nft family values [#65295](https://github.com/saltstack/salt/issues/65295) +- pkg.installed state aggregate does not honors requires requisite [#65304](https://github.com/saltstack/salt/issues/65304) +- Added SSH wrapper for logmod [#65630](https://github.com/saltstack/salt/issues/65630) +- Fix for GitFS failure to unlock lock file, and resource cleanup for process SIGTERM [#65816](https://github.com/saltstack/salt/issues/65816) +- Corrected x509_v2 CRL creation `last_update` and `next_update` values when system timezone is not UTC [#65837](https://github.com/saltstack/salt/issues/65837) +- Make sure the root minion process handles SIGUSR1 and emits a traceback like it's child processes [#66095](https://github.com/saltstack/salt/issues/66095) +- Replaced pyvenv with builtin venv for virtualenv_mod [#66132](https://github.com/saltstack/salt/issues/66132) +- Made `file.managed` skip download of a remote source if the managed file already exists with the correct hash [#66342](https://github.com/saltstack/salt/issues/66342) +- Fix win_task ExecutionTimeLimit and result/error code interpretation [#66347](https://github.com/saltstack/salt/issues/66347), [#66441](https://github.com/saltstack/salt/issues/66441) +- Fixed nftables.build_rule breaks ipv6 rules by using the wrong syntax for source and destination addresses [#66382](https://github.com/saltstack/salt/issues/66382) +- Fixed x509_v2 certificate.managed crash for locally signed certificates if the signing policy defines signing_private_key [#66414](https://github.com/saltstack/salt/issues/66414) +- Fixed parallel state execution with Salt-SSH [#66514](https://github.com/saltstack/salt/issues/66514) +- Fix support for FIPS approved encryption and signing algorithms. [#66579](https://github.com/saltstack/salt/issues/66579) +- Fix relative file_roots paths [#66588](https://github.com/saltstack/salt/issues/66588) +- Fixed an issue with cmd.run with requirements when the shell is not the + default [#66596](https://github.com/saltstack/salt/issues/66596) +- Fix RPM package provides [#66604](https://github.com/saltstack/salt/issues/66604) +- Upgrade relAenv to 0.16.1. This release fixes several package installs for salt-pip [#66632](https://github.com/saltstack/salt/issues/66632) +- Upgrade relenv to 0.17.0 (https://github.com/saltstack/relenv/blob/v0.17.0/CHANGELOG.md) [#66663](https://github.com/saltstack/salt/issues/66663) +- Upgrade dependencies due to security issues: + - pymysql>=1.1.1 + - requests>=2.32.0 + - docker>=7.1.0 [#66666](https://github.com/saltstack/salt/issues/66666) +- Corrected missed line in branch 3006.x when backporting from PR 61620 and 65044 [#66683](https://github.com/saltstack/salt/issues/66683) +- Remove debug output from shell scripts for packaging [#66747](https://github.com/saltstack/salt/issues/66747) + + +### Added + +- Add Ubuntu 24.04 support [#66180](https://github.com/saltstack/salt/issues/66180) +- Add Fedora 40 support, replacing Fedora 39 [#66300](https://github.com/saltstack/salt/issues/66300) +- Build RPM packages with Rocky Linux 9 (instead of CentOS Stream 9) [#66624](https://github.com/saltstack/salt/issues/66624) + + +### Security + +- Bump to ``jinja2==3.1.4`` due to https://github.com/advisories/GHSA-h75v-3vvj-5mfj [#66488](https://github.com/saltstack/salt/issues/66488) +- CVE-2024-37088 salt-call will fail with exit code 1 if bad pillar data is + encountered. [#66702](https://github.com/saltstack/salt/issues/66702) diff --git a/doc/topics/releases/templates/3006.9.md.template b/doc/topics/releases/templates/3006.9.md.template new file mode 100644 index 00000000000..6bcb03dd59c --- /dev/null +++ b/doc/topics/releases/templates/3006.9.md.template @@ -0,0 +1,14 @@ +(release-3006.9)= +# Salt 3006.9 release notes{{ unreleased }} +{{ warning }} + + + + +## Changelog +{{ changelog }} diff --git a/noxfile.py b/noxfile.py index fa7f95fe866..4d33c8f8180 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1870,18 +1870,10 @@ def ci_test_onedir_pkgs(session): "--upgrade", "--no-uninstall", ], - "upgrade-classic": [ - "--upgrade", - "--no-uninstall", - ], "downgrade": [ "--downgrade", "--no-uninstall", ], - "downgrade-classic": [ - "--downgrade", - "--no-uninstall", - ], "download-pkgs": [ "--download-pkgs", ], @@ -1912,9 +1904,6 @@ def ci_test_onedir_pkgs(session): "PKG_TEST_TYPE": chunk, } - if chunk in ("upgrade-classic", "downgrade-classic"): - cmd_args.append("--classic") - pytest_args = ( common_pytest_args[:] + cmd_args[:] @@ -1982,8 +1971,6 @@ def ci_test_onedir_pkgs(session): ) if "downgrade" in chunk: pytest_args.append("--use-prev-version") - if chunk in ("upgrade-classic", "downgrade-classic"): - pytest_args.append("--classic") if append_tests_path: pytest_args.append("tests/pytests/pkg/") try: @@ -2006,8 +1993,6 @@ def ci_test_onedir_pkgs(session): ) if "downgrade" in chunk: pytest_args.append("--use-prev-version") - if chunk in ("upgrade-classic", "downgrade-classic"): - pytest_args.append("--classic") if append_tests_path: pytest_args.append("tests/pytests/pkg/") _pytest( diff --git a/pkg/debian/changelog b/pkg/debian/changelog index d90880dfb95..0e8f39d4f45 100644 --- a/pkg/debian/changelog +++ b/pkg/debian/changelog @@ -42,6 +42,70 @@ salt (3007.1) stable; urgency=medium -- Salt Project Packaging Sun, 19 May 2024 12:48:59 +0000 +salt (3006.9) stable; urgency=medium + + + # Deprecated + + * Drop CentOS 7 support [#66623](https://github.com/saltstack/salt/issues/66623) + * No longer build RPM packages with CentOS Stream 9 [#66624](https://github.com/saltstack/salt/issues/66624) + + # Fixed + + * Made slsutil.renderer work with salt-ssh [#50196](https://github.com/saltstack/salt/issues/50196) + * Fixed defaults.merge is not available when using salt-ssh [#51605](https://github.com/saltstack/salt/issues/51605) + * Fixed config.get does not support merge option with salt-ssh [#56441](https://github.com/saltstack/salt/issues/56441) + * Update to include croniter in pkg requirements [#57649](https://github.com/saltstack/salt/issues/57649) + * Fixed state.test does not work with salt-ssh [#61100](https://github.com/saltstack/salt/issues/61100) + * Made slsutil.findup work with salt-ssh [#61143](https://github.com/saltstack/salt/issues/61143) + * file.replace and file.search work properly with /proc files [#63102](https://github.com/saltstack/salt/issues/63102) + * Fix utf8 handling in 'pass' renderer [#64300](https://github.com/saltstack/salt/issues/64300) + * Fixed incorrect version argument will be ignored for multiple package targets warning when using pkgs argument to yumpkg module. [#64563](https://github.com/saltstack/salt/issues/64563) + * salt-cloud honors root_dir config setting for log_file location and fixes for root_dir locations on windows. [#64728](https://github.com/saltstack/salt/issues/64728) + * Fixed slsutil.update with salt-ssh during template rendering [#65067](https://github.com/saltstack/salt/issues/65067) + * Fix config.items when called on minion [#65251](https://github.com/saltstack/salt/issues/65251) + * Ensure on rpm and deb systems, that user and group for existing Salt, is maintained on upgrade [#65264](https://github.com/saltstack/salt/issues/65264) + * Fix typo in nftables module to ensure unique nft family values [#65295](https://github.com/saltstack/salt/issues/65295) + * pkg.installed state aggregate does not honors requires requisite [#65304](https://github.com/saltstack/salt/issues/65304) + * Added SSH wrapper for logmod [#65630](https://github.com/saltstack/salt/issues/65630) + * Fix for GitFS failure to unlock lock file, and resource cleanup for process SIGTERM [#65816](https://github.com/saltstack/salt/issues/65816) + * Corrected x509_v2 CRL creation `last_update` and `next_update` values when system timezone is not UTC [#65837](https://github.com/saltstack/salt/issues/65837) + * Make sure the root minion process handles SIGUSR1 and emits a traceback like it's child processes [#66095](https://github.com/saltstack/salt/issues/66095) + * Replaced pyvenv with builtin venv for virtualenv_mod [#66132](https://github.com/saltstack/salt/issues/66132) + * Made `file.managed` skip download of a remote source if the managed file already exists with the correct hash [#66342](https://github.com/saltstack/salt/issues/66342) + * Fix win_task ExecutionTimeLimit and result/error code interpretation [#66347](https://github.com/saltstack/salt/issues/66347), [#66441](https://github.com/saltstack/salt/issues/66441) + * Fixed nftables.build_rule breaks ipv6 rules by using the wrong syntax for source and destination addresses [#66382](https://github.com/saltstack/salt/issues/66382) + * Fixed x509_v2 certificate.managed crash for locally signed certificates if the signing policy defines signing_private_key [#66414](https://github.com/saltstack/salt/issues/66414) + * Fixed parallel state execution with Salt-SSH [#66514](https://github.com/saltstack/salt/issues/66514) + * Fix support for FIPS approved encryption and signing algorithms. [#66579](https://github.com/saltstack/salt/issues/66579) + * Fix relative file_roots paths [#66588](https://github.com/saltstack/salt/issues/66588) + * Fixed an issue with cmd.run with requirements when the shell is not the + default [#66596](https://github.com/saltstack/salt/issues/66596) + * Fix RPM package provides [#66604](https://github.com/saltstack/salt/issues/66604) + * Upgrade relAenv to 0.16.1. This release fixes several package installs for salt-pip [#66632](https://github.com/saltstack/salt/issues/66632) + * Upgrade relenv to 0.17.0 (https://github.com/saltstack/relenv/blob/v0.17.0/CHANGELOG.md) [#66663](https://github.com/saltstack/salt/issues/66663) + * Upgrade dependencies due to security issues: + * pymysql>=1.1.1 + * requests>=2.32.0 + * docker>=7.1.0 [#66666](https://github.com/saltstack/salt/issues/66666) + * Corrected missed line in branch 3006.x when backporting from PR 61620 and 65044 [#66683](https://github.com/saltstack/salt/issues/66683) + * Remove debug output from shell scripts for packaging [#66747](https://github.com/saltstack/salt/issues/66747) + + # Added + + * Add Ubuntu 24.04 support [#66180](https://github.com/saltstack/salt/issues/66180) + * Add Fedora 40 support, replacing Fedora 39 [#66300](https://github.com/saltstack/salt/issues/66300) + * Build RPM packages with Rocky Linux 9 (instead of CentOS Stream 9) [#66624](https://github.com/saltstack/salt/issues/66624) + + # Security + + * Bump to ``jinja2==3.1.4`` due to https://github.com/advisories/GHSA-h75v-3vvj-5mfj [#66488](https://github.com/saltstack/salt/issues/66488) + * CVE-2024-37088 salt-call will fail with exit code 1 if bad pillar data is + encountered. [#66702](https://github.com/saltstack/salt/issues/66702) + + + -- Salt Project Packaging Mon, 29 Jul 2024 07:42:36 +0000 + salt (3006.8) stable; urgency=medium diff --git a/pkg/debian/salt-api.postinst b/pkg/debian/salt-api.postinst index 9345d72bf2a..3b78211922a 100644 --- a/pkg/debian/salt-api.postinst +++ b/pkg/debian/salt-api.postinst @@ -1,10 +1,37 @@ +#!/bin/sh + +. /usr/share/debconf/confmodule + case "$1" in configure) - if [ ! -e "/var/log/salt/api" ]; then - touch /var/log/salt/api - chmod 640 /var/log/salt/api + db_get salt-api/user + if [ "$RET" != "root" ]; then + if [ ! -e "/var/log/salt/api" ]; then + touch /var/log/salt/api + chmod 640 /var/log/salt/api + fi + chown $RET:$RET /var/log/salt/api + fi + if command -v systemctl; then + db_get salt-api/active + RESLT=$(echo "$RET" | cut -d ' ' -f 1) + if [ "$RESLT" != 10 ]; then + systemctl daemon-reload + if [ "$RESLT" = "active" ]; then + systemctl restart salt-api + fi + db_get salt-api/enabled + RESLT=$(echo "$RET" | cut -d ' ' -f 1) + if [ "$RESLT" = "disabled" ]; then + systemctl disable salt-api + else + systemctl enable salt-api + fi + else + systemctl daemon-reload + systemctl restart salt-api + systemctl enable salt-api + fi fi - chown salt:salt /var/log/salt/api - if command -v systemctl; then systemctl enable salt-api; fi ;; esac diff --git a/pkg/debian/salt-api.preinst b/pkg/debian/salt-api.preinst new file mode 100644 index 00000000000..ddc7c9e0ec7 --- /dev/null +++ b/pkg/debian/salt-api.preinst @@ -0,0 +1,28 @@ +#!/bin/sh + +. /usr/share/debconf/confmodule + +case "$1" in + upgrade) + [ -z "$SALT_HOME" ] && SALT_HOME=/opt/saltstack/salt + [ -z "$SALT_USER" ] && SALT_USER=salt + [ -z "$SALT_NAME" ] && SALT_NAME="Salt" + [ -z "$SALT_GROUP" ] && SALT_GROUP=salt + + # Reset permissions to fix previous installs + CUR_USER=$(ls -dl /run/salt-api.pid | cut -d ' ' -f 3) + CUR_GROUP=$(ls -dl /run/salt-api.pid | cut -d ' ' -f 4) + db_set salt-api/user $CUR_USER + chown -R $CUR_USER:$CUR_GROUP /var/log/salt/api + if command -v systemctl; then + SM_ENABLED=$(systemctl show -p UnitFileState salt-api | cut -d '=' -f 2) + db_set salt-api/enabled $SM_ENABLED + SM_ACTIVE=$(systemctl is-active salt-api) + db_set salt-api/active $SM_ACTIVE + else + db_set salt-api/enabled enabled + db_set salt-api/active active + + fi + ;; +esac diff --git a/pkg/debian/salt-api.templates b/pkg/debian/salt-api.templates new file mode 100644 index 00000000000..88e4b0823c7 --- /dev/null +++ b/pkg/debian/salt-api.templates @@ -0,0 +1,17 @@ +Template: salt-api/user +Type: string +Default: salt +Description: User for salt-api + User to run the salt-api process as + +Template: salt-api/enabled +Type: string +Default: enabled +Description: Systemd enable state for salt-api + default enable state for salt-api systemd state + +Template: salt-api/active +Type: string +Default: active +Description: Systemd active state for salt-api + default active state for salt-api systemd state diff --git a/pkg/debian/salt-cloud.postinst b/pkg/debian/salt-cloud.postinst index a92551161da..a6c3c2119a9 100644 --- a/pkg/debian/salt-cloud.postinst +++ b/pkg/debian/salt-cloud.postinst @@ -1,6 +1,13 @@ +#!/bin/sh + +. /usr/share/debconf/confmodule + case "$1" in configure) - PY_VER=$(/opt/saltstack/salt/bin/python3 -c "import sys; sys.stdout.write('{}.{}'.format(*sys.version_info)); sys.stdout.flush;") - chown -R salt:salt /etc/salt/cloud.deploy.d /opt/saltstack/salt/lib/python${PY_VER}/site-packages/salt/cloud/deploy + db_get salt-master/user + if [ "$RET" != "root" ]; then + PY_VER=$(/opt/saltstack/salt/bin/python3 -c "import sys; sys.stdout.write('{}.{}'.format(*sys.version_info)); sys.stdout.flush;") + chown -R $RET:$RET /etc/salt/cloud.deploy.d /opt/saltstack/salt/lib/python${PY_VER}/site-packages/salt/cloud/deploy + fi ;; esac diff --git a/pkg/debian/salt-master.postinst b/pkg/debian/salt-master.postinst index 4f7686d8ed9..be7064f9bad 100644 --- a/pkg/debian/salt-master.postinst +++ b/pkg/debian/salt-master.postinst @@ -1,14 +1,41 @@ +#!/bin/sh + +. /usr/share/debconf/confmodule + case "$1" in configure) - if [ ! -e "/var/log/salt/master" ]; then - touch /var/log/salt/master - chmod 640 /var/log/salt/master + db_get salt-master/user + if [ "$RET" != "root" ]; then + if [ ! -e "/var/log/salt/master" ]; then + touch /var/log/salt/master + chmod 640 /var/log/salt/master + fi + if [ ! -e "/var/log/salt/key" ]; then + touch /var/log/salt/key + chmod 640 /var/log/salt/key + fi + chown -R $RET:$RET /etc/salt/pki/master /etc/salt/master.d /var/log/salt/master /var/log/salt/key /var/cache/salt/master /var/run/salt/master fi - if [ ! -e "/var/log/salt/key" ]; then - touch /var/log/salt/key - chmod 640 /var/log/salt/key + if command -v systemctl; then + db_get salt-master/active + RESLT=$(echo "$RET" | cut -d ' ' -f 1) + if [ "$RESLT" != 10 ]; then + systemctl daemon-reload + if [ "$RESLT" = "active" ]; then + systemctl restart salt-master + fi + db_get salt-master/enabled + RESLT=$(echo "$RET" | cut -d ' ' -f 1) + if [ "$RESLT" = "disabled" ]; then + systemctl disable salt-master + else + systemctl enable salt-master + fi + else + systemctl daemon-reload + systemctl restart salt-master + systemctl enable salt-master + fi fi - chown -R salt:salt /etc/salt/pki/master /etc/salt/master.d /var/log/salt/master /var/log/salt/key /var/cache/salt/master /var/run/salt/master - if command -v systemctl; then systemctl enable salt-master; fi ;; esac diff --git a/pkg/debian/salt-master.preinst b/pkg/debian/salt-master.preinst index f205423079c..af978b8e508 100644 --- a/pkg/debian/salt-master.preinst +++ b/pkg/debian/salt-master.preinst @@ -1,5 +1,9 @@ +#!/bin/sh + +. /usr/share/debconf/confmodule + case "$1" in - install|upgrade) + install) [ -z "$SALT_HOME" ] && SALT_HOME=/opt/saltstack/salt [ -z "$SALT_USER" ] && SALT_USER=salt [ -z "$SALT_NAME" ] && SALT_NAME="Salt" @@ -8,11 +12,37 @@ case "$1" in # Reset permissions to fix previous installs find ${SALT_HOME} /etc/salt /var/log/salt /var/cache/salt /var/run/salt \ - \! \( -path /etc/salt/cloud.deploy.d\* -o -path /var/log/salt/cloud -o -path /opt/saltstack/salt/lib/python${PY_VER}/site-packages/salt/cloud/deploy\* \) -a \ - \( -user ${SALT_USER} -o -group ${SALT_GROUP} \) -exec chown root:root \{\} \; + \! \( -path /etc/salt/cloud.deploy.d\* -o -path /var/log/salt/cloud -o -path \ + /opt/saltstack/salt/lib/python${PY_VER}/site-packages/salt/cloud/deploy\* \) -a \( -user ${SALT_USER} \ + -o -group ${SALT_GROUP} \) -exec chown ${SALT_USER}:${SALT_GROUP} \{\} \; - # remove incorrectly installed ufw salt-master directory - issue 57712 - test -d /etc/ufw/applications.d/salt-master && rm -rf /etc/ufw/applications.d/salt-master || /bin/true + ;; - ;; + upgrade) + [ -z "$SALT_HOME" ] && SALT_HOME=/opt/saltstack/salt + [ -z "$SALT_USER" ] && SALT_USER=salt + [ -z "$SALT_NAME" ] && SALT_NAME="Salt" + [ -z "$SALT_GROUP" ] && SALT_GROUP=salt + PY_VER=$(/opt/saltstack/salt/bin/python3 -c "import sys; sys.stdout.write('{}.{}'.format(*sys.version_info)); sys.stdout.flush();") + + # Reset permissions to fix previous installs + CUR_USER=$(ls -dl /run/salt/master | cut -d ' ' -f 3) + CUR_GROUP=$(ls -dl /run/salt/master | cut -d ' ' -f 4) + db_set salt-master/user $CUR_USER + chown -R $CUR_USER:$CUR_GROUP /etc/salt/pki/master /etc/salt/master.d /var/log/salt/master \ + /var/log/salt/key /var/cache/salt/master /var/run/salt/master + if command -v systemctl; then + SM_ENABLED=$(systemctl show -p UnitFileState salt-master | cut -d '=' -f 2) + db_set salt-master/enabled $SM_ENABLED + SM_ACTIVE=$(systemctl is-active salt-master) + db_set salt-master/active $SM_ACTIVE + else + db_set salt-master/enabled enabled + db_set salt-master/active active + + fi + ;; esac + +# remove incorrectly installed ufw salt-master directory - issue 57712 +test -d /etc/ufw/applications.d/salt-master && rm -rf /etc/ufw/applications.d/salt-master || /bin/true diff --git a/pkg/debian/salt-master.templates b/pkg/debian/salt-master.templates new file mode 100644 index 00000000000..c0ea8cfd69b --- /dev/null +++ b/pkg/debian/salt-master.templates @@ -0,0 +1,17 @@ +Template: salt-master/user +Type: string +Default: salt +Description: User for salt-master + User to run the salt-master process as + +Template: salt-master/enabled +Type: string +Default: enabled +Description: Systemd enable state for salt-master + default enable state for salt-master systemd state + +Template: salt-master/active +Type: string +Default: active +Description: Systemd active state for salt-master + default active state for salt-master systemd state diff --git a/pkg/debian/salt-minion.postinst b/pkg/debian/salt-minion.postinst new file mode 100644 index 00000000000..13d1cf50901 --- /dev/null +++ b/pkg/debian/salt-minion.postinst @@ -0,0 +1,41 @@ +#!/bin/sh + +. /usr/share/debconf/confmodule + +case "$1" in + configure) + db_get salt-minion/user + if [ "$RET" != "root" ]; then + if [ ! -e "/var/log/salt/minion" ]; then + touch /var/log/salt/minion + chmod 640 /var/log/salt/minion + fi + if [ ! -e "/var/log/salt/key" ]; then + touch /var/log/salt/key + chmod 640 /var/log/salt/key + fi + chown -R $RET:$RET /etc/salt/pki/minion /etc/salt/minion.d /var/log/salt/minion /var/cache/salt/minion /var/run/salt/minion + fi + if command -v systemctl; then + db_get salt-minion/active + RESLT=$(echo "$RET" | cut -d ' ' -f 1) + if [ "$RESLT" != 10 ]; then + systemctl daemon-reload + if [ "$RESLT" = "active" ]; then + systemctl restart salt-minion + fi + db_get salt-minion/enabled + RESLT=$(echo "$RET" | cut -d ' ' -f 1) + if [ "$RESLT" = "disabled" ]; then + systemctl disable salt-minion + else + systemctl enable salt-minion + fi + else + systemctl daemon-reload + systemctl restart salt-minion + systemctl enable salt-minion + fi + fi + ;; +esac diff --git a/pkg/debian/salt-minion.preinst b/pkg/debian/salt-minion.preinst new file mode 100644 index 00000000000..4a4cd949c64 --- /dev/null +++ b/pkg/debian/salt-minion.preinst @@ -0,0 +1,30 @@ +#!/bin/sh + +. /usr/share/debconf/confmodule + +case "$1" in + upgrade) + [ -z "$SALT_HOME" ] && SALT_HOME=/opt/saltstack/salt + [ -z "$SALT_USER" ] && SALT_USER=salt + [ -z "$SALT_NAME" ] && SALT_NAME="Salt" + [ -z "$SALT_GROUP" ] && SALT_GROUP=salt + PY_VER=$(/opt/saltstack/salt/bin/python3 -c "import sys; sys.stdout.write('{}.{}'.format(*sys.version_info)); sys.stdout.flush();") + + # Reset permissions to fix previous installs + CUR_USER=$(ls -dl /run/salt/minion | cut -d ' ' -f 3) + CUR_GROUP=$(ls -dl /run/salt/minion | cut -d ' ' -f 4) + db_set salt-minion/user $CUR_USER + chown -R $CUR_USER:$CUR_GROUP /etc/salt/pki/minion /etc/salt/minion.d /var/log/salt/minion \ + /var/cache/salt/minion /var/run/salt/minion + if command -v systemctl; then + SM_ENABLED=$(systemctl show -p UnitFileState salt-minion | cut -d '=' -f 2) + db_set salt-minion/enabled $SM_ENABLED + SM_ACTIVE=$(systemctl is-active salt-minion) + db_set salt-minion/active $SM_ACTIVE + else + db_set salt-minion/enabled enabled + db_set salt-minion/active active + + fi + ;; +esac diff --git a/pkg/debian/salt-minion.templates b/pkg/debian/salt-minion.templates new file mode 100644 index 00000000000..583e027d5d7 --- /dev/null +++ b/pkg/debian/salt-minion.templates @@ -0,0 +1,17 @@ +Template: salt-minion/user +Type: string +Default: root +Description: User for salt-minion + User to run the salt-minion process as + +Template: salt-minion/enabled +Type: string +Default: enabled +Description: Systemd enable state for salt-minion + default enable state for salt-minion systemd state + +Template: salt-minion/active +Type: string +Default: active +Description: Systemd active state for salt-minion + default active state for salt-minion systemd state diff --git a/pkg/rpm/salt.spec b/pkg/rpm/salt.spec index 637b3f65079..7c1aef06818 100644 --- a/pkg/rpm/salt.spec +++ b/pkg/rpm/salt.spec @@ -15,10 +15,18 @@ %global __requires_exclude_from ^.*$ %define _source_payload w2.gzdio %define _binary_payload w2.gzdio -%define _SALT_GROUP salt -%define _SALT_USER salt -%define _SALT_NAME Salt -%define _SALT_HOME /opt/saltstack/salt +%global _SALT_GROUP salt +%global _SALT_USER salt +%global _SALT_NAME Salt +%global _SALT_HOME /opt/saltstack/salt + +# salt-master current user and group +%global _MS_CUR_USER %{_SALT_USER} +%global _MS_CUR_GROUP %{_SALT_GROUP} + +# salt-minion current user and group +%global _MN_CUR_USER %{_SALT_USER} +%global _MN_CUR_GROUP %{_SALT_GROUP} # Disable debugsource template %define _debugsource_template %{nil} @@ -425,11 +433,22 @@ usermod -c "%{_SALT_NAME}" \ %{_SALT_USER} %pre master -# Reset permissions to fix previous installs -PY_VER=$(/opt/saltstack/salt/bin/python3 -c "import sys; sys.stdout.write('{}.{}'.format(*sys.version_info)); sys.stdout.flush();") -find /etc/salt /opt/saltstack/salt /var/log/salt /var/cache/salt /var/run/salt \ - \! \( -path /etc/salt/cloud.deploy.d\* -o -path /var/log/salt/cloud -o -path /opt/saltstack/salt/lib/python${PY_VER}/site-packages/salt/cloud/deploy\* \) -a \ - \( -user salt -o -group salt \) -exec chown root:root \{\} \; +if [ $1 -gt 1 ] ; then + # Reset permissions to match previous installs - performing upgrade + _MS_LCUR_USER=$(ls -dl /run/salt/master | cut -d ' ' -f 3) + _MS_LCUR_GROUP=$(ls -dl /run/salt/master | cut -d ' ' -f 4) + %global _MS_CUR_USER %{_MS_LCUR_USER} + %global _MS_CUR_GROUP %{_MS_LCUR_GROUP} +fi + +%pre minion +if [ $1 -gt 1 ] ; then + # Reset permissions to match previous installs - performing upgrade + _MN_LCUR_USER=$(ls -dl /run/salt/minion | cut -d ' ' -f 3) + _MN_LCUR_GROUP=$(ls -dl /run/salt/minion | cut -d ' ' -f 4) + %global _MN_CUR_USER %{_MN_LCUR_USER} + %global _MN_CUR_GROUP %{_MN_LCUR_GROUP} +fi # assumes systemd for RHEL 7 & 8 & 9 @@ -558,7 +577,12 @@ if [ ! -e "/var/log/salt/cloud" ]; then touch /var/log/salt/cloud chmod 640 /var/log/salt/cloud fi -chown -R %{_SALT_USER}:%{_SALT_GROUP} /etc/salt/cloud.deploy.d /var/log/salt/cloud /opt/saltstack/salt/lib/python${PY_VER}/site-packages/salt/cloud/deploy +if [ $1 -gt 1 ] ; then + # Reset permissions to match previous installs - performing upgrade + chown -R %{_MS_CUR_USER}:%{_MS_CUR_GROUP} /etc/salt/cloud.deploy.d /var/log/salt/cloud /opt/saltstack/salt/lib/python${PY_VER}/site-packages/salt/cloud/deploy +else + chown -R %{_SALT_USER}:%{_SALT_GROUP} /etc/salt/cloud.deploy.d /var/log/salt/cloud /opt/saltstack/salt/lib/python${PY_VER}/site-packages/salt/cloud/deploy +fi %posttrans master @@ -570,7 +594,12 @@ if [ ! -e "/var/log/salt/key" ]; then touch /var/log/salt/key chmod 640 /var/log/salt/key fi -chown -R %{_SALT_USER}:%{_SALT_GROUP} /etc/salt/pki/master /etc/salt/master.d /var/log/salt/master /var/log/salt/key /var/cache/salt/master /var/run/salt/master +if [ $1 -gt 1 ] ; then + # Reset permissions to match previous installs - performing upgrade + chown -R %{_MS_CUR_USER}:%{_MS_CUR_GROUP} /etc/salt/pki/master /etc/salt/master.d /var/log/salt/master /var/log/salt/key /var/cache/salt/master /var/run/salt/master +else + chown -R %{_SALT_USER}:%{_SALT_GROUP} /etc/salt/pki/master /etc/salt/master.d /var/log/salt/master /var/log/salt/key /var/cache/salt/master /var/run/salt/master +fi %posttrans api @@ -578,7 +607,26 @@ if [ ! -e "/var/log/salt/api" ]; then touch /var/log/salt/api chmod 640 /var/log/salt/api fi -chown %{_SALT_USER}:%{_SALT_GROUP} /var/log/salt/api +if [ $1 -gt 1 ] ; then + # Reset permissions to match previous installs - performing upgrade + chown -R %{_MS_CUR_USER}:%{_MS_CUR_GROUP} /var/log/salt/api +else + chown -R %{_SALT_USER}:%{_SALT_GROUP} /var/log/salt/api +fi + +%posttrans minion +if [ ! -e "/var/log/salt/minion" ]; then + touch /var/log/salt/minion + chmod 640 /var/log/salt/minion +fi +if [ ! -e "/var/log/salt/key" ]; then + touch /var/log/salt/key + chmod 640 /var/log/salt/key +fi +if [ $1 -gt 1 ] ; then + # Reset permissions to match previous installs - performing upgrade + chown -R %{_MN_CUR_USER}:%{_MN_CUR_GROUP} /etc/salt/pki/minion /etc/salt/minion.d /var/log/salt/minion /var/cache/salt/minion /var/run/salt/minion +fi %preun @@ -688,6 +736,74 @@ fi - Bump to ``jinja2==3.1.4`` due to https://github.com/advisories/GHSA-h75v-3vvj-5mfj [#66488](https://github.com/saltstack/salt/issues/66488) +* Mon Jul 29 2024 Salt Project Packaging - 3006.9 + +# Deprecated + +- Drop CentOS 7 support [#66623](https://github.com/saltstack/salt/issues/66623) +- No longer build RPM packages with CentOS Stream 9 [#66624](https://github.com/saltstack/salt/issues/66624) + +# Fixed + +- Made slsutil.renderer work with salt-ssh [#50196](https://github.com/saltstack/salt/issues/50196) +- Fixed defaults.merge is not available when using salt-ssh [#51605](https://github.com/saltstack/salt/issues/51605) +- Fixed config.get does not support merge option with salt-ssh [#56441](https://github.com/saltstack/salt/issues/56441) +- Update to include croniter in pkg requirements [#57649](https://github.com/saltstack/salt/issues/57649) +- Fixed state.test does not work with salt-ssh [#61100](https://github.com/saltstack/salt/issues/61100) +- Made slsutil.findup work with salt-ssh [#61143](https://github.com/saltstack/salt/issues/61143) +- Fixes multiple issues with the cmd module on Windows. Scripts are called using + the ``-File`` parameter to the ``powershell.exe`` binary. ``CLIXML`` data in + stderr is now removed (only applies to encoded commands). Commands can now be + sent to ``cmd.powershell`` as a list. Makes sure JSON data returned is valid. + Strips whitespace from the return when using ``runas``. [#61166](https://github.com/saltstack/salt/issues/61166) +- Fixed the win_lgpo_netsh salt util to handle non-English systems. This was a + rewrite to use PowerShell instead of netsh to make the changes on the system [#61534](https://github.com/saltstack/salt/issues/61534) +- file.replace and file.search work properly with /proc files [#63102](https://github.com/saltstack/salt/issues/63102) +- Fix utf8 handling in 'pass' renderer [#64300](https://github.com/saltstack/salt/issues/64300) +- Fixed incorrect version argument will be ignored for multiple package targets warning when using pkgs argument to yumpkg module. [#64563](https://github.com/saltstack/salt/issues/64563) +- salt-cloud honors root_dir config setting for log_file location and fixes for root_dir locations on windows. [#64728](https://github.com/saltstack/salt/issues/64728) +- Fixed slsutil.update with salt-ssh during template rendering [#65067](https://github.com/saltstack/salt/issues/65067) +- Fix config.items when called on minion [#65251](https://github.com/saltstack/salt/issues/65251) +- Ensure on rpm and deb systems, that user and group for existing Salt, is maintained on upgrade [#65264](https://github.com/saltstack/salt/issues/65264) +- Fix typo in nftables module to ensure unique nft family values [#65295](https://github.com/saltstack/salt/issues/65295) +- pkg.installed state aggregate does not honors requires requisite [#65304](https://github.com/saltstack/salt/issues/65304) +- Added SSH wrapper for logmod [#65630](https://github.com/saltstack/salt/issues/65630) +- Fix for GitFS failure to unlock lock file, and resource cleanup for process SIGTERM [#65816](https://github.com/saltstack/salt/issues/65816) +- Corrected x509_v2 CRL creation `last_update` and `next_update` values when system timezone is not UTC [#65837](https://github.com/saltstack/salt/issues/65837) +- Make sure the root minion process handles SIGUSR1 and emits a traceback like it's child processes [#66095](https://github.com/saltstack/salt/issues/66095) +- Replaced pyvenv with builtin venv for virtualenv_mod [#66132](https://github.com/saltstack/salt/issues/66132) +- Made `file.managed` skip download of a remote source if the managed file already exists with the correct hash [#66342](https://github.com/saltstack/salt/issues/66342) +- Fix win_task ExecutionTimeLimit and result/error code interpretation [#66347](https://github.com/saltstack/salt/issues/66347), [#66441](https://github.com/saltstack/salt/issues/66441) +- Fixed nftables.build_rule breaks ipv6 rules by using the wrong syntax for source and destination addresses [#66382](https://github.com/saltstack/salt/issues/66382) +- Fixed x509_v2 certificate.managed crash for locally signed certificates if the signing policy defines signing_private_key [#66414](https://github.com/saltstack/salt/issues/66414) +- Fixed parallel state execution with Salt-SSH [#66514](https://github.com/saltstack/salt/issues/66514) +- Fix support for FIPS approved encryption and signing algorithms. [#66579](https://github.com/saltstack/salt/issues/66579) +- Fix relative file_roots paths [#66588](https://github.com/saltstack/salt/issues/66588) +- Fixed an issue with cmd.run with requirements when the shell is not the + default [#66596](https://github.com/saltstack/salt/issues/66596) +- Fix RPM package provides [#66604](https://github.com/saltstack/salt/issues/66604) +- Upgrade relAenv to 0.16.1. This release fixes several package installs for salt-pip [#66632](https://github.com/saltstack/salt/issues/66632) +- Upgrade relenv to 0.17.0 (https://github.com/saltstack/relenv/blob/v0.17.0/CHANGELOG.md) [#66663](https://github.com/saltstack/salt/issues/66663) +- Upgrade dependencies due to security issues: + - pymysql>=1.1.1 + - requests>=2.32.0 + - docker>=7.1.0 [#66666](https://github.com/saltstack/salt/issues/66666) +- Corrected missed line in branch 3006.x when backporting from PR 61620 and 65044 [#66683](https://github.com/saltstack/salt/issues/66683) +- Remove debug output from shell scripts for packaging [#66747](https://github.com/saltstack/salt/issues/66747) + +# Added + +- Add Ubuntu 24.04 support [#66180](https://github.com/saltstack/salt/issues/66180) +- Add Fedora 40 support, replacing Fedora 39 [#66300](https://github.com/saltstack/salt/issues/66300) +- Build RPM packages with Rocky Linux 9 (instead of CentOS Stream 9) [#66624](https://github.com/saltstack/salt/issues/66624) + +# Security + +- Bump to ``jinja2==3.1.4`` due to https://github.com/advisories/GHSA-h75v-3vvj-5mfj [#66488](https://github.com/saltstack/salt/issues/66488) +- CVE-2024-37088 salt-call will fail with exit code 1 if bad pillar data is + encountered. [#66702](https://github.com/saltstack/salt/issues/66702) + + * Mon Apr 29 2024 Salt Project Packaging - 3006.8 # Removed @@ -950,6 +1066,7 @@ fi # Fixed +- Fix issue with ownership on upgrade of master and minion files - Fix an issue with mac_shadow that was causing a command execution error when retrieving values that were not yet set. For example, retrieving last login before the user had logged in. [#34658](https://github.com/saltstack/salt/issues/34658) diff --git a/pkg/windows/nsis/installer/helper_StrContains.nsh b/pkg/windows/nsis/installer/helper_StrContains.nsh new file mode 100644 index 00000000000..bea8ac45146 --- /dev/null +++ b/pkg/windows/nsis/installer/helper_StrContains.nsh @@ -0,0 +1,52 @@ +#------------------------------------------------------------------------------ +# StrContains +# +# This function does a case sensitive searches for an occurrence of a substring in a string. +# It returns the substring if it is found. +# Otherwise it returns null(""). +# Written by kenglish_hi +# Adapted from StrReplace written by dandaman32 +#------------------------------------------------------------------------------ +!define StrContains "!insertmacro StrContains" +!macro StrContains OUT NEEDLE HAYSTACK + Push "${HAYSTACK}" + Push "${NEEDLE}" + Call StrContains + Pop "${OUT}" +!macroend +Function StrContains + + # Initialize variables + Var /GLOBAL STR_HAYSTACK + Var /GLOBAL STR_NEEDLE + Var /GLOBAL STR_CONTAINS_VAR_1 + Var /GLOBAL STR_CONTAINS_VAR_2 + Var /GLOBAL STR_CONTAINS_VAR_3 + Var /GLOBAL STR_CONTAINS_VAR_4 + Var /GLOBAL STR_RETURN_VAR + + Exch $STR_NEEDLE + Exch 1 + Exch $STR_HAYSTACK + # Uncomment to debug + #MessageBox MB_OK 'STR_NEEDLE = $STR_NEEDLE STR_HAYSTACK = $STR_HAYSTACK ' + StrCpy $STR_RETURN_VAR "" + StrCpy $STR_CONTAINS_VAR_1 -1 + StrLen $STR_CONTAINS_VAR_2 $STR_NEEDLE + StrLen $STR_CONTAINS_VAR_4 $STR_HAYSTACK + + loop: + IntOp $STR_CONTAINS_VAR_1 $STR_CONTAINS_VAR_1 + 1 + StrCpy $STR_CONTAINS_VAR_3 $STR_HAYSTACK $STR_CONTAINS_VAR_2 $STR_CONTAINS_VAR_1 + StrCmp $STR_CONTAINS_VAR_3 $STR_NEEDLE found + StrCmp $STR_CONTAINS_VAR_1 $STR_CONTAINS_VAR_4 done + Goto loop + + found: + StrCpy $STR_RETURN_VAR $STR_NEEDLE + Goto done + + done: + Pop $STR_NEEDLE # Prevent "invalid opcode" errors and keep the stack clean + Exch $STR_RETURN_VAR +FunctionEnd diff --git a/pkg/windows/nsis/tests/clean.ps1 b/pkg/windows/nsis/tests/clean.ps1 index 9cc7bb49e8b..a9065d41dcb 100644 --- a/pkg/windows/nsis/tests/clean.ps1 +++ b/pkg/windows/nsis/tests/clean.ps1 @@ -12,8 +12,17 @@ clean.ps1 clean.ps1 #> +param( + [Parameter(Mandatory=$false)] + [Alias("c")] +# Don't pretify the output of the Write-Result + [Switch] $CICD +) +#------------------------------------------------------------------------------- # Script Preferences +#------------------------------------------------------------------------------- + $ProgressPreference = "SilentlyContinue" $ErrorActionPreference = "Stop" @@ -21,15 +30,22 @@ $ErrorActionPreference = "Stop" # Script Variables #------------------------------------------------------------------------------- -$SCRIPT_DIR = (Get-ChildItem "$($myInvocation.MyCommand.Definition)").DirectoryName +$SCRIPT_DIR = (Get-ChildItem "$($myInvocation.MyCommand.Definition)").DirectoryName +$PROJECT_DIR = $(git rev-parse --show-toplevel) +$WINDOWS_DIR = "$PROJECT_DIR\pkg\windows" +$BUILDENV_DIR = "$WINDOWS_DIR\buildenv" #------------------------------------------------------------------------------- # Script Functions #------------------------------------------------------------------------------- function Write-Result($result, $ForegroundColor="Green") { - $position = 80 - $result.Length - [System.Console]::CursorLeft - Write-Host -ForegroundColor $ForegroundColor ("{0,$position}$result" -f "") + if ( $CICD ) { + Write-Host $result -ForegroundColor $ForegroundColor + } else { + $position = 80 - $result.Length - [System.Console]::CursorLeft + Write-Host -ForegroundColor $ForegroundColor ("{0,$position}$result" -f "") + } } #------------------------------------------------------------------------------- @@ -61,6 +77,51 @@ if ( Test-Path -Path "$SCRIPT_DIR\venv" ) { Write-Result "Success" -ForegroundColor Green } } + +#------------------------------------------------------------------------------- +# Remove buildenv directory +#------------------------------------------------------------------------------- +if ( Test-Path -Path "$BUILDENV_DIR" ) { + Write-Host "Removing buildenv directory: " -NoNewline + Remove-Item -Path "$BUILDENV_DIR" -Recurse -Force + if ( Test-Path -Path "$BUILDENV_DIR" ) { + Write-Result "Failed" -ForegroundColor Red + exit 1 + } else { + Write-Result "Success" -ForegroundColor Green + } +} + +#------------------------------------------------------------------------------- +# Make sure processes are not running +#------------------------------------------------------------------------------- +$processes = "test-setup", + "Un", + "Un_A", + "Un_B", + "Un_C", + "Un_D", + "Un_E", + "Un_F", + "Un_G" +$processes | ForEach-Object { + $proc = Get-Process -Name $_ -ErrorAction SilentlyContinue + if ( ($null -ne $proc) ) { + Write-Host "Killing $($_): " -NoNewline + $proc = Get-WmiObject -Class Win32_Process -Filter "Name='$_.exe'" + $proc.Terminate() *> $null + Start-Sleep -Seconds 5 + $proc = Get-Process -Name $_ -ErrorAction SilentlyContinue + if ( ($null -eq $proc) ) { + Write-Result "Success" -ForegroundColor Green + } else { + Write-Result "Failed" -ForegroundColor Red + exit 1 + } + } +} + + #------------------------------------------------------------------------------- # Remove test-setup.exe #------------------------------------------------------------------------------- @@ -75,6 +136,92 @@ if ( Test-Path -Path "$SCRIPT_DIR\test-setup.exe" ) { } } +#------------------------------------------------------------------------------- +# Remove custom_conf +#------------------------------------------------------------------------------- +if ( Test-Path -Path "$SCRIPT_DIR\custom_conf" ) { + Write-Host "Removing custom_conf: " -NoNewline + Remove-Item -Path "$SCRIPT_DIR\custom_conf" -Recurse -Force + if ( Test-Path -Path "$SCRIPT_DIR\custom_conf" ) { + Write-Result "Failed" -ForegroundColor Red + exit 1 + } else { + Write-Result "Success" -ForegroundColor Green + } +} + +#------------------------------------------------------------------------------- +# Remove the salt-minion service +#------------------------------------------------------------------------------- +if ( $(Get-Service -Name salt-minion -ErrorAction SilentlyContinue).Name ) { + Write-Host "Removing salt-minion service" -NoNewline + Stop-Service -Name salt-minion + $service = Get-WmiObject -Class Win32_Service -Filter "Name='salt-minion'" + $service.delete() *> $null + if ( $(Get-Service -Name salt-minion -ErrorAction SilentlyContinue).Name ) { + Write-Result "Failed" -ForegroundColor Red + exit 1 + } else { + Write-Result "Success" -ForegroundColor Green + } +} + +#------------------------------------------------------------------------------- +# Remove Salt Project directory from Program Files +#------------------------------------------------------------------------------- +if ( Test-Path -Path "$env:ProgramFiles\Salt Project" ) { + Write-Host "Removing Salt Project from Program Files: " -NoNewline + Remove-Item -Path "$env:ProgramFiles\Salt Project" -Recurse -Force + if ( Test-Path -Path "$env:ProgramFiles\Salt Project" ) { + Write-Result "Failed" -ForegroundColor Red + exit 1 + } else { + Write-Result "Success" -ForegroundColor Green + } +} + +#------------------------------------------------------------------------------- +# Remove Salt Project directory from ProgramData +#------------------------------------------------------------------------------- +if ( Test-Path -Path "$env:ProgramData\Salt Project" ) { + Write-Host "Removing Salt Project from ProgramData: " -NoNewline + Remove-Item -Path "$env:ProgramData\Salt Project" -Recurse -Force + if ( Test-Path -Path "$env:ProgramData\Salt Project" ) { + Write-Result "Failed" -ForegroundColor Red + exit 1 + } else { + Write-Result "Success" -ForegroundColor Green + } +} + +#------------------------------------------------------------------------------- +# Remove Salt Project from Registry +#------------------------------------------------------------------------------- +if ( Test-Path -Path "HKLM:SOFTWARE\Salt Project" ) { + Write-Host "Removing Salt Project from Software: " -NoNewline + Remove-Item -Path "HKLM:SOFTWARE\Salt Project" -Recurse -Force + if ( Test-Path -Path "HKLM:SOFTWARE\Salt Project" ) { + Write-Result "Failed" -ForegroundColor Red + exit 1 + } else { + Write-Result "Success" -ForegroundColor Green + } +} + +#------------------------------------------------------------------------------- +# Remove Salt Minion directory from Registry +#------------------------------------------------------------------------------- +if ( Test-Path -Path "HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Salt Minion" ) { + Write-Host "Removing Salt Minion from the Uninstall: " -NoNewline + Remove-Item -Path "HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Salt Minion" -Recurse -Force + if ( Test-Path -Path "HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Salt Minion" ) { + Write-Result "Failed" -ForegroundColor Red + exit 1 + } else { + Write-Result "Success" -ForegroundColor Green + } +} + #------------------------------------------------------------------------------- # Script Completed #------------------------------------------------------------------------------- diff --git a/pkg/windows/nsis/tests/config_tests/test_custom_full_path.py b/pkg/windows/nsis/tests/config_tests/test_custom_full_path.py index 05f186f6c1b..8239b548f21 100644 --- a/pkg/windows/nsis/tests/config_tests/test_custom_full_path.py +++ b/pkg/windows/nsis/tests/config_tests/test_custom_full_path.py @@ -6,32 +6,38 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create a custom config - pytest.helpers.custom_config() - - full_path_conf = rf"{pytest.REPO_DIR}\custom_conf" - - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", f"/custom-config={full_path_conf}"] - ) - yield + full_path_conf = pytest.helpers.custom_config() + # Install salt with custom config + args = ["/S", f"/custom-config={full_path_conf}"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): - assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + data_dir = pytest.DATA_DIR + data_dir_exists = os.path.exists(data_dir) + assert os.path.exists(rf"{data_dir}\conf\minion") def test_config_correct(install): # The config file should be the custom config, unchanged - with open(rf"{pytest.REPO_DIR}\custom_conf") as f: + script_dir = pytest.SCRIPT_DIR + script_dir_exists = os.path.exists(script_dir) + with open(rf"{script_dir}\custom_conf") as f: expected = f.readlines() + data_dir = pytest.DATA_DIR + data_dir_exists = os.path.exists(data_dir) with open(rf"{pytest.DATA_DIR}\conf\minion") as f: result = f.readlines() diff --git a/pkg/windows/nsis/tests/config_tests/test_custom_master.py b/pkg/windows/nsis/tests/config_tests/test_custom_master.py index d332fea1964..d3bcfa65fd5 100644 --- a/pkg/windows/nsis/tests/config_tests/test_custom_master.py +++ b/pkg/windows/nsis/tests/config_tests/test_custom_master.py @@ -6,19 +6,21 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", "/custom-config=custom_conf", "/master=cli_master"] - ) - yield + # Install salt with custom config + args = ["/S", "/custom-config=custom_conf", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/config_tests/test_custom_master_minion.py b/pkg/windows/nsis/tests/config_tests/test_custom_master_minion.py index 0a862c12c72..4f8e4891486 100644 --- a/pkg/windows/nsis/tests/config_tests/test_custom_master_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_custom_master_minion.py @@ -6,25 +6,26 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - "/custom-config=custom_conf", - "/master=cli_master", - "/minion-name=cli_minion", - ] - ) - yield + # Install salt with custom config + args = [ + "/S", + "/custom-config=custom_conf", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/config_tests/test_custom_minion.py b/pkg/windows/nsis/tests/config_tests/test_custom_minion.py index 4274defbb5d..36c6595e192 100644 --- a/pkg/windows/nsis/tests/config_tests/test_custom_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_custom_minion.py @@ -6,24 +6,21 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - "/custom-config=custom_conf", - "/minion-name=cli_minion", - ] - ) - yield + # Install salt with custom config + args = ["/S", "/custom-config=custom_conf", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/config_tests/test_custom_rel_path.py b/pkg/windows/nsis/tests/config_tests/test_custom_rel_path.py index a54969faf20..7948b04eeba 100644 --- a/pkg/windows/nsis/tests/config_tests/test_custom_rel_path.py +++ b/pkg/windows/nsis/tests/config_tests/test_custom_rel_path.py @@ -6,17 +6,21 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command([pytest.INST_BIN, "/S", "/custom-config=custom_conf"]) - yield + # Install salt with custom config + args = ["/S", "/custom-config=custom_conf"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): @@ -25,7 +29,7 @@ def test_config_present(install): def test_config_correct(install): # The config file should be the custom config, unchanged - with open(rf"{pytest.REPO_DIR}\custom_conf") as f: + with open(rf"{pytest.SCRIPT_DIR}\custom_conf") as f: expected = f.readlines() with open(rf"{pytest.DATA_DIR}\conf\minion") as f: diff --git a/pkg/windows/nsis/tests/config_tests/test_default.py b/pkg/windows/nsis/tests/config_tests/test_default.py index 7681b44e52b..70ad7a1165b 100644 --- a/pkg/windows/nsis/tests/config_tests/test_default.py +++ b/pkg/windows/nsis/tests/config_tests/test_default.py @@ -6,13 +6,18 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - pytest.helpers.run_command([pytest.INST_BIN, "/S"]) - yield + args = ["/S"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): @@ -21,7 +26,7 @@ def test_config_present(install): def test_config_correct(install): # The config file should be the default config, unchanged - with open(rf"{pytest.REPO_DIR}\_files\minion") as f: + with open(rf"{pytest.SCRIPT_DIR}\_files\minion") as f: expected = f.readlines() with open(rf"{pytest.DATA_DIR}\conf\minion") as f: diff --git a/pkg/windows/nsis/tests/config_tests/test_default_master.py b/pkg/windows/nsis/tests/config_tests/test_default_master.py index d978f7b206a..9bdfca3ffe9 100644 --- a/pkg/windows/nsis/tests/config_tests/test_default_master.py +++ b/pkg/windows/nsis/tests/config_tests/test_default_master.py @@ -6,13 +6,18 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - pytest.helpers.run_command([pytest.INST_BIN, "/S", "/master=cli_master"]) - yield + args = ["/S", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/config_tests/test_default_master_minion.py b/pkg/windows/nsis/tests/config_tests/test_default_master_minion.py index 4a23c362995..b04896a000f 100644 --- a/pkg/windows/nsis/tests/config_tests/test_default_master_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_default_master_minion.py @@ -6,15 +6,18 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", "/master=cli_master", "/minion-name=cli_minion"] - ) - yield + args = ["/S", "/master=cli_master", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/config_tests/test_default_minion.py b/pkg/windows/nsis/tests/config_tests/test_default_minion.py index c3f165a8ef9..7959c26e29b 100644 --- a/pkg/windows/nsis/tests/config_tests/test_default_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_default_minion.py @@ -6,13 +6,18 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - pytest.helpers.run_command([pytest.INST_BIN, "/S", "/minion-name=cli_minion"]) - yield + args = ["/S", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/config_tests/test_existing.py b/pkg/windows/nsis/tests/config_tests/test_existing.py index 64baced527e..479792d0172 100644 --- a/pkg/windows/nsis/tests/config_tests/test_existing.py +++ b/pkg/windows/nsis/tests/config_tests/test_existing.py @@ -6,17 +6,20 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - - pytest.helpers.run_command([pytest.INST_BIN, "/S"]) - yield + args = ["/S"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/config_tests/test_existing_custom.py b/pkg/windows/nsis/tests/config_tests/test_existing_custom.py index 6fb147739f2..46c552d0b9e 100644 --- a/pkg/windows/nsis/tests/config_tests/test_existing_custom.py +++ b/pkg/windows/nsis/tests/config_tests/test_existing_custom.py @@ -6,20 +6,22 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command([pytest.INST_BIN, "/S", "/custom-config=custom_conf"]) - yield + args = ["/S", "/custom-config=custom_conf"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): @@ -28,7 +30,7 @@ def test_config_present(install): def test_config_correct(install): # The config file should be the custom config, unchanged - with open(rf"{pytest.REPO_DIR}\custom_conf") as f: + with open(rf"{pytest.SCRIPT_DIR}\custom_conf") as f: expected = f.readlines() with open(rf"{pytest.DATA_DIR}\conf\minion") as f: diff --git a/pkg/windows/nsis/tests/config_tests/test_existing_custom_master.py b/pkg/windows/nsis/tests/config_tests/test_existing_custom_master.py index 78e80cea265..3eb53d45cf1 100644 --- a/pkg/windows/nsis/tests/config_tests/test_existing_custom_master.py +++ b/pkg/windows/nsis/tests/config_tests/test_existing_custom_master.py @@ -6,22 +6,22 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", "/custom-config=custom_conf", "/master=cli_master"] - ) - yield + args = ["/S", "/custom-config=custom_conf", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/config_tests/test_existing_custom_master_minion.py b/pkg/windows/nsis/tests/config_tests/test_existing_custom_master_minion.py index 66e36c41262..bb6ad116ca0 100644 --- a/pkg/windows/nsis/tests/config_tests/test_existing_custom_master_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_existing_custom_master_minion.py @@ -6,28 +6,27 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - "/custom-config=custom_conf", - "/master=cli_master", - "/minion-name=cli_minion", - ] - ) - yield + args = [ + "/S", + "/custom-config=custom_conf", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/config_tests/test_existing_custom_minion.py b/pkg/windows/nsis/tests/config_tests/test_existing_custom_minion.py index 545e8219537..a7f8e342452 100644 --- a/pkg/windows/nsis/tests/config_tests/test_existing_custom_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_existing_custom_minion.py @@ -8,24 +8,20 @@ def install(): pytest.helpers.clean_env() # Create an existing config pytest.helpers.existing_config() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - "/custom-config=custom_conf", - "/minion-name=cli_minion", - ] - ) - yield + args = ["/S", "/custom-config=custom_conf", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/config_tests/test_existing_default.py b/pkg/windows/nsis/tests/config_tests/test_existing_default.py index aaaa2622e02..fba4e1a1c06 100644 --- a/pkg/windows/nsis/tests/config_tests/test_existing_default.py +++ b/pkg/windows/nsis/tests/config_tests/test_existing_default.py @@ -6,17 +6,20 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - - pytest.helpers.run_command([pytest.INST_BIN, "/S", "/default-config"]) - yield + args = ["/S", "/default-config"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): @@ -25,7 +28,7 @@ def test_config_present(install): def test_config_correct(install): # The config file should be the default config, unchanged - with open(rf"{pytest.REPO_DIR}\_files\minion") as f: + with open(rf"{pytest.SCRIPT_DIR}\_files\minion") as f: expected = f.readlines() with open(rf"{pytest.DATA_DIR}\conf\minion") as f: diff --git a/pkg/windows/nsis/tests/config_tests/test_existing_default_master.py b/pkg/windows/nsis/tests/config_tests/test_existing_default_master.py index 456080e4996..dc02133f8c1 100644 --- a/pkg/windows/nsis/tests/config_tests/test_existing_default_master.py +++ b/pkg/windows/nsis/tests/config_tests/test_existing_default_master.py @@ -6,19 +6,20 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", "/default-config", "/master=cli_master"] - ) - yield + args = ["/S", "/default-config", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/config_tests/test_existing_default_master_minion.py b/pkg/windows/nsis/tests/config_tests/test_existing_default_master_minion.py index 90d262e9b0e..74b95290a8b 100644 --- a/pkg/windows/nsis/tests/config_tests/test_existing_default_master_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_existing_default_master_minion.py @@ -6,25 +6,20 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - "/default-config", - "/master=cli_master", - "/minion-name=cli_minion", - ] - ) - yield + args = ["/S", "/default-config", "/master=cli_master", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/config_tests/test_existing_default_minion.py b/pkg/windows/nsis/tests/config_tests/test_existing_default_minion.py index 16e41035c66..cdc22e421e9 100644 --- a/pkg/windows/nsis/tests/config_tests/test_existing_default_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_existing_default_minion.py @@ -6,19 +6,20 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", "/default-config", "/minion-name=cli_minion"] - ) - yield + args = ["/S", "/default-config", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(rf"{pytest.INST_DIR}\ssm.exe") + # This will show the contents of the directory on failure + inst_dir = pytest.INST_DIR + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/config_tests/test_install_dir_custom.py b/pkg/windows/nsis/tests/config_tests/test_install_dir_custom.py index a2fce64fe36..e11895122bc 100644 --- a/pkg/windows/nsis/tests/config_tests/test_install_dir_custom.py +++ b/pkg/windows/nsis/tests/config_tests/test_install_dir_custom.py @@ -10,24 +10,19 @@ def inst_dir(): @pytest.fixture(scope="module") def install(inst_dir): - pytest.helpers.clean_env(inst_dir) - + pytest.helpers.clean_env() # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - f"/install-dir={inst_dir}", - "/custom-config=custom_conf", - ] - ) - yield + args = ["/S", f"/install-dir={inst_dir}", "/custom-config=custom_conf"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env(inst_dir) def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) assert os.path.exists(rf"{inst_dir}\ssm.exe") @@ -37,7 +32,7 @@ def test_config_present(install): def test_config_correct(install): # The config file should be the custom config, unchanged - with open(rf"{pytest.REPO_DIR}\custom_conf") as f: + with open(rf"{pytest.SCRIPT_DIR}\custom_conf") as f: expected = f.readlines() with open(rf"{pytest.DATA_DIR}\conf\minion") as f: diff --git a/pkg/windows/nsis/tests/config_tests/test_install_dir_custom_master.py b/pkg/windows/nsis/tests/config_tests/test_install_dir_custom_master.py index d47abd1df83..267bf516eb9 100644 --- a/pkg/windows/nsis/tests/config_tests/test_install_dir_custom_master.py +++ b/pkg/windows/nsis/tests/config_tests/test_install_dir_custom_master.py @@ -10,25 +10,24 @@ def inst_dir(): @pytest.fixture(scope="module") def install(inst_dir): - pytest.helpers.clean_env(inst_dir) - + pytest.helpers.clean_env() # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - f"/install-dir={inst_dir}", - "/custom-config=custom_conf", - "/master=cli_master", - ] - ) - yield + args = [ + "/S", + f"/install-dir={inst_dir}", + "/custom-config=custom_conf", + "/master=cli_master", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env(inst_dir) def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) assert os.path.exists(rf"{inst_dir}\ssm.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_install_dir_custom_master_minion.py b/pkg/windows/nsis/tests/config_tests/test_install_dir_custom_master_minion.py index bf039af4c20..a6919a822c7 100644 --- a/pkg/windows/nsis/tests/config_tests/test_install_dir_custom_master_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_install_dir_custom_master_minion.py @@ -10,26 +10,25 @@ def inst_dir(): @pytest.fixture(scope="module") def install(inst_dir): - pytest.helpers.clean_env(inst_dir) - + pytest.helpers.clean_env() # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - f"/install-dir={inst_dir}", - "/custom-config=custom_conf", - "/master=cli_master", - "/minion-name=cli_minion", - ] - ) - yield + args = [ + "/S", + f"/install-dir={inst_dir}", + "/custom-config=custom_conf", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env(inst_dir) def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) assert os.path.exists(rf"{inst_dir}\ssm.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_install_dir_custom_minion.py b/pkg/windows/nsis/tests/config_tests/test_install_dir_custom_minion.py index fcdf2146e0a..7f46ea88def 100644 --- a/pkg/windows/nsis/tests/config_tests/test_install_dir_custom_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_install_dir_custom_minion.py @@ -10,25 +10,24 @@ def inst_dir(): @pytest.fixture(scope="module") def install(inst_dir): - pytest.helpers.clean_env(inst_dir) - + pytest.helpers.clean_env() # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - f"/install-dir={inst_dir}", - "/custom-config=custom_conf", - "/minion-name=cli_minion", - ] - ) - yield + args = [ + "/S", + f"/install-dir={inst_dir}", + "/custom-config=custom_conf", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env(inst_dir) def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) assert os.path.exists(rf"{inst_dir}\ssm.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_install_dir_default.py b/pkg/windows/nsis/tests/config_tests/test_install_dir_default.py index ac3c403e2e6..f5674685243 100644 --- a/pkg/windows/nsis/tests/config_tests/test_install_dir_default.py +++ b/pkg/windows/nsis/tests/config_tests/test_install_dir_default.py @@ -10,13 +10,17 @@ def inst_dir(): @pytest.fixture(scope="module") def install(inst_dir): - pytest.helpers.clean_env(inst_dir) - pytest.helpers.run_command([pytest.INST_BIN, "/S", f"/install-dir={inst_dir}"]) - yield + pytest.helpers.clean_env() + args = ["/S", f"/install-dir={inst_dir}"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env(inst_dir) def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) assert os.path.exists(rf"{inst_dir}\ssm.exe") @@ -26,7 +30,7 @@ def test_config_present(install): def test_config_correct(install): # The config file should be the default config, unchanged - with open(rf"{pytest.REPO_DIR}\_files\minion") as f: + with open(rf"{pytest.SCRIPT_DIR}\_files\minion") as f: expected = f.readlines() with open(rf"{pytest.DATA_DIR}\conf\minion") as f: diff --git a/pkg/windows/nsis/tests/config_tests/test_install_dir_default_master.py b/pkg/windows/nsis/tests/config_tests/test_install_dir_default_master.py index 2ac36f87e27..2de9fe76a95 100644 --- a/pkg/windows/nsis/tests/config_tests/test_install_dir_default_master.py +++ b/pkg/windows/nsis/tests/config_tests/test_install_dir_default_master.py @@ -5,20 +5,22 @@ import pytest @pytest.fixture(scope="module") def inst_dir(): - return r"C:\custom_location" + return "C:\\custom_location" @pytest.fixture(scope="module") def install(inst_dir): - pytest.helpers.clean_env(inst_dir) - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", f"/install-dir={inst_dir}", "/master=cli_master"] - ) - yield + pytest.helpers.clean_env() + args = ["/S", f"/install-dir={inst_dir}", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env(inst_dir) def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) assert os.path.exists(rf"{inst_dir}\ssm.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_install_dir_default_master_minion.py b/pkg/windows/nsis/tests/config_tests/test_install_dir_default_master_minion.py index a2996764dd6..9ec9d329bc2 100644 --- a/pkg/windows/nsis/tests/config_tests/test_install_dir_default_master_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_install_dir_default_master_minion.py @@ -5,26 +5,27 @@ import pytest @pytest.fixture(scope="module") def inst_dir(): - return r"C:\custom_location" + return "C:\\custom_location" @pytest.fixture(scope="module") def install(inst_dir): - pytest.helpers.clean_env(inst_dir) - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - f"/install-dir={inst_dir}", - "/master=cli_master", - "/minion-name=cli_minion", - ] - ) - yield + pytest.helpers.clean_env() + args = [ + "/S", + f"/install-dir={inst_dir}", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env(inst_dir) def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) assert os.path.exists(rf"{inst_dir}\ssm.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_install_dir_default_minion.py b/pkg/windows/nsis/tests/config_tests/test_install_dir_default_minion.py index 6f8f9dc8590..dbe73e7e24c 100644 --- a/pkg/windows/nsis/tests/config_tests/test_install_dir_default_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_install_dir_default_minion.py @@ -5,20 +5,22 @@ import pytest @pytest.fixture(scope="module") def inst_dir(): - return r"C:\custom_location" + return "C:\\custom_location" @pytest.fixture(scope="module") def install(inst_dir): - pytest.helpers.clean_env(inst_dir) - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", f"/install-dir={inst_dir}", "/minion-name=cli_minion"] - ) - yield {"inst_dir": inst_dir} + pytest.helpers.clean_env() + args = ["/S", f"/install-dir={inst_dir}", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env(inst_dir) def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) assert os.path.exists(rf"{inst_dir}\ssm.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_install_dir_existing.py b/pkg/windows/nsis/tests/config_tests/test_install_dir_existing.py index f28a61de692..f785dc2d94c 100644 --- a/pkg/windows/nsis/tests/config_tests/test_install_dir_existing.py +++ b/pkg/windows/nsis/tests/config_tests/test_install_dir_existing.py @@ -5,22 +5,24 @@ import pytest @pytest.fixture(scope="module") def inst_dir(): - return r"C:\custom_location" + return "C:\\custom_location" @pytest.fixture(scope="module") def install(inst_dir): - pytest.helpers.clean_env(inst_dir) - + pytest.helpers.clean_env() # Create an existing config pytest.helpers.existing_config() - - pytest.helpers.run_command([pytest.INST_BIN, "/S", f"/install-dir={inst_dir}"]) - yield + args = ["/S", f"/install-dir={inst_dir}"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env(inst_dir) def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) assert os.path.exists(rf"{inst_dir}\ssm.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_install_dir_move_old_install.py b/pkg/windows/nsis/tests/config_tests/test_install_dir_move_old_install.py index a556a2c3a98..b2423996600 100644 --- a/pkg/windows/nsis/tests/config_tests/test_install_dir_move_old_install.py +++ b/pkg/windows/nsis/tests/config_tests/test_install_dir_move_old_install.py @@ -5,24 +5,23 @@ import pytest @pytest.fixture(scope="module") def inst_dir(): - return r"C:\custom_location" + return "C:\\custom_location" @pytest.fixture(scope="module") def install(inst_dir): pytest.helpers.clean_env() - # Create old install pytest.helpers.old_install() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", f"/install-dir={inst_dir}", "/move-config"] - ) - yield + args = ["/S", f"/install-dir={inst_dir}", "/move-config"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") # Apparently we don't move the binaries even if they pass install-dir # TODO: Decide if this is expected behavior assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install.py b/pkg/windows/nsis/tests/config_tests/test_old_install.py index f4ac2f8204f..2db0aa56e4e 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install.py @@ -6,20 +6,20 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - - pytest.helpers.run_command([pytest.INST_BIN, "/S"]) - yield + args = ["/S"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_custom.py b/pkg/windows/nsis/tests/config_tests/test_old_install_custom.py index 4e3dfdf3f78..3c792052acc 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_custom.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_custom.py @@ -6,23 +6,22 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command([pytest.INST_BIN, "/S", "/custom-config=custom_conf"]) - yield + args = ["/S", "/custom-config=custom_conf"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") @@ -32,7 +31,7 @@ def test_config_present_old_location(install): def test_config_correct(install): # The config file should be the custom config, unchanged - with open(rf"{pytest.REPO_DIR}\custom_conf") as f: + with open(rf"{pytest.SCRIPT_DIR}\custom_conf") as f: expected = f.readlines() with open(rf"{pytest.OLD_DIR}\conf\minion") as f: diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_custom_master.py b/pkg/windows/nsis/tests/config_tests/test_old_install_custom_master.py index 441221e9841..094642bf899 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_custom_master.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_custom_master.py @@ -6,25 +6,22 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", "/custom-config=custom_conf", "/master=cli_master"] - ) - yield + args = ["/S", "/custom-config=custom_conf", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_custom_master_minion.py b/pkg/windows/nsis/tests/config_tests/test_old_install_custom_master_minion.py index 0eb22436351..bee83a7ee9e 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_custom_master_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_custom_master_minion.py @@ -6,31 +6,27 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - "/custom-config=custom_conf", - "/master=cli_master", - "/minion-name=cli_minion", - ] - ) - yield + args = [ + "/S", + "/custom-config=custom_conf", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_custom_minion.py b/pkg/windows/nsis/tests/config_tests/test_old_install_custom_minion.py index 0f265fd4fbe..e542f799a4c 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_custom_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_custom_minion.py @@ -6,25 +6,22 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", "/custom-config=custom_conf", "/minion-name=cli_minion"] - ) - yield + args = ["/S", "/custom-config=custom_conf", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_default.py b/pkg/windows/nsis/tests/config_tests/test_old_install_default.py index d3aed4f5fb1..e5a85d6ceb5 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_default.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_default.py @@ -6,20 +6,20 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - - # Create old config + # Create old install pytest.helpers.old_install() - - pytest.helpers.run_command([pytest.INST_BIN, "/S", "/default-config"]) - yield + args = ["/S", "/default-config"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") @@ -29,7 +29,7 @@ def test_config_present_old_location(install): def test_config_correct(install): # The config file should be the default config, unchanged - with open(rf"{pytest.REPO_DIR}\_files\minion") as f: + with open(rf"{pytest.SCRIPT_DIR}\_files\minion") as f: expected = f.readlines() with open(rf"{pytest.OLD_DIR}\conf\minion") as f: diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_default_master.py b/pkg/windows/nsis/tests/config_tests/test_old_install_default_master.py index e0546759046..998dc23c57f 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_default_master.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_default_master.py @@ -6,22 +6,20 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", "/default-config", "/master=cli_master"] - ) - yield + args = ["/S", "/default-config", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_default_master_minion.py b/pkg/windows/nsis/tests/config_tests/test_old_install_default_master_minion.py index c4f418a4a19..a08306c7911 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_default_master_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_default_master_minion.py @@ -6,28 +6,25 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - "/default-config", - "/master=cli_master", - "/minion-name=cli_minion", - ] - ) - yield + args = [ + "/S", + "/default-config", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_default_minion.py b/pkg/windows/nsis/tests/config_tests/test_old_install_default_minion.py index 563b8125b48..5365800667e 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_default_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_default_minion.py @@ -6,22 +6,20 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", "/default-config", "/minion-name=cli_minion"] - ) - yield + args = ["/S", "/default-config", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_move.py b/pkg/windows/nsis/tests/config_tests/test_old_install_move.py index d5328d82b3e..aaf1a10f9f3 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_move.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_move.py @@ -6,24 +6,24 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - - pytest.helpers.run_command([pytest.INST_BIN, "/S", "/move-config"]) - yield + args = ["/S", "/move-config"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") -def test_config_present_old_location(install): +def test_config_present_new_location(install): assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom.py b/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom.py index eadd2fd10fa..11ef683ea3f 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom.py @@ -6,35 +6,32 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", "/custom-config=custom_conf", "/move-config"] - ) - yield + args = ["/S", "/custom-config=custom_conf", "/move-config"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") -def test_config_present_old_location(install): +def test_config_present_new_location(install): assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") def test_config_correct(install): # The config file should be the custom config in the new location, unchanged - with open(rf"{pytest.REPO_DIR}\custom_conf") as f: + with open(rf"{pytest.SCRIPT_DIR}\custom_conf") as f: expected = f.readlines() with open(rf"{pytest.DATA_DIR}\conf\minion") as f: diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom_master.py b/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom_master.py index 5d0aa67d52a..9698bcdce4a 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom_master.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom_master.py @@ -6,35 +6,26 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - "/custom-config=custom_conf", - "/move-config", - "/master=cli_master", - ] - ) - yield + args = ["/S", "/custom-config=custom_conf", "/move-config", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") -def test_config_present_old_location(install): +def test_config_present_new_location(install): assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom_master_minion.py b/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom_master_minion.py index da3e2916eb6..01ab2117630 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom_master_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom_master_minion.py @@ -6,36 +6,32 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - "/custom-config=custom_conf", - "/move-config", - "/master=cli_master", - "/minion-name=cli_minion", - ] - ) - yield + args = [ + "/S", + "/custom-config=custom_conf", + "/move-config", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") -def test_config_present_old_location(install): +def test_config_present_new_location(install): assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom_minion.py b/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom_minion.py index 621c840ccd0..e3c4ed35b15 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_move_custom_minion.py @@ -6,35 +6,31 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - "/custom-config=custom_conf", - "/move-config", - "/minion-name=cli_minion", - ] - ) - yield + args = [ + "/S", + "/custom-config=custom_conf", + "/move-config", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") -def test_config_present_old_location(install): +def test_config_present_new_location(install): assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_move_default.py b/pkg/windows/nsis/tests/config_tests/test_old_install_move_default.py index 216a0b40c50..b0151e83b8f 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_move_default.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_move_default.py @@ -6,32 +6,30 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", "/move-config", "/default-config"] - ) - yield + args = ["/S", "/move-config", "/default-config"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") -def test_config_present_old_location(install): +def test_config_present_new_location(install): assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") def test_config_correct(install): # The config file should be the default config in the new location, unchanged - with open(rf"{pytest.REPO_DIR}\_files\minion") as f: + with open(rf"{pytest.SCRIPT_DIR}\_files\minion") as f: expected = f.readlines() with open(rf"{pytest.DATA_DIR}\conf\minion") as f: diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_move_default_master.py b/pkg/windows/nsis/tests/config_tests/test_old_install_move_default_master.py index 1d2d2a158c5..73c09cf6851 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_move_default_master.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_move_default_master.py @@ -6,26 +6,24 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/S", "/default-config", "/move-config", "/master=cli_master"] - ) - yield + args = ["/S", "/default-config", "/move-config", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") -def test_config_present_old_location(install): +def test_config_present_new_location(install): assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_move_default_master_minion.py b/pkg/windows/nsis/tests/config_tests/test_old_install_move_default_master_minion.py index a46e1e5243d..95fd52594e1 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_move_default_master_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_move_default_master_minion.py @@ -6,33 +6,30 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - "/default-config", - "/move-config", - "/master=cli_master", - "/minion-name=cli_minion", - ] - ) - yield + args = [ + "/S", + "/default-config", + "/move-config", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") -def test_config_present_old_location(install): +def test_config_present_new_location(install): assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") diff --git a/pkg/windows/nsis/tests/config_tests/test_old_install_move_default_minion.py b/pkg/windows/nsis/tests/config_tests/test_old_install_move_default_minion.py index 2fdada9f21f..3691b2366d3 100644 --- a/pkg/windows/nsis/tests/config_tests/test_old_install_move_default_minion.py +++ b/pkg/windows/nsis/tests/config_tests/test_old_install_move_default_minion.py @@ -6,32 +6,29 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create old config pytest.helpers.old_install() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/S", - "/default-config", - "/move-config", - "/minion-name=cli_minion", - ] - ) - yield + args = [ + "/S", + "/default-config", + "/move-config", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() -def test_ssm_present_old_location(install): - assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") - - def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") -def test_config_present_old_location(install): +def test_config_present_new_location(install): assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") diff --git a/pkg/windows/nsis/tests/conftest.py b/pkg/windows/nsis/tests/conftest.py index 5fe43bdb428..be44a7c2221 100644 --- a/pkg/windows/nsis/tests/conftest.py +++ b/pkg/windows/nsis/tests/conftest.py @@ -1,4 +1,5 @@ import os +import re import shutil import subprocess import time @@ -49,16 +50,33 @@ INST_DIR = r"C:\Program Files\Salt Project\Salt" DATA_DIR = r"C:\ProgramData\Salt Project\Salt" SYSTEM_DRIVE = os.environ.get("SystemDrive") OLD_DIR = f"{SYSTEM_DRIVE}\\salt" +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) +INST_BIN = rf"{SCRIPT_DIR}\test-setup.exe" +PROCESSES = [ + os.path.basename(INST_BIN), + "uninst.exe", + "Un.exe", + "Un_A.exe", + "Un_B.exe", + "Un_C.exe", + "Un_D.exe", + "Un_D.exe", + "Un_F.exe", + "Un_G.exe", +] def reg_key_exists(hive=winreg.HKEY_LOCAL_MACHINE, key=None): + """ + Helper function to determine if a registry key exists. It does this by + opening the key. If the connection is successful, the key exists. Otherwise + an error is returned, which means the key does not exist + """ try: with winreg.OpenKey(hive, key, 0, winreg.KEY_READ): - exists = True + return True except: - exists = False - - return exists + return False def delete_key(hive=winreg.HKEY_LOCAL_MACHINE, key=None): @@ -66,60 +84,97 @@ def delete_key(hive=winreg.HKEY_LOCAL_MACHINE, key=None): parent, _, base = key.rpartition("\\") with winreg.OpenKey(hive, parent, 0, winreg.KEY_ALL_ACCESS) as reg: winreg.DeleteKey(reg, base) + assert not reg_key_exists(hive=hive, key=key) def pytest_configure(): pytest.DATA_DIR = DATA_DIR pytest.INST_DIR = INST_DIR - pytest.REPO_DIR = REPO_DIR pytest.INST_BIN = INST_BIN pytest.OLD_DIR = OLD_DIR + pytest.SCRIPT_DIR = SCRIPT_DIR pytest.EXISTING_CONTENT = existing_content pytest.CUSTOM_CONTENT = custom_content pytest.OLD_CONTENT = old_content -@pytest.helpers.register -def clean_env(inst_dir=INST_DIR): - # Run uninstaller - for uninst_bin in [f"{inst_dir}\\uninst.exe", f"{OLD_DIR}\\uninst.exe"]: - if os.path.exists(uninst_bin): - run_command([uninst_bin, "/S", "/delete-root-dir", "/delete-install-dir"]) - # This is needed to avoid a race condition where the uninstall is completing - start_time = time.time() - while "Un_A.exe" in (p.name() for p in psutil.process_iter()): - # Sometimes the Uninstall binary hangs... we'll kill it after 10 seconds - if (time.time() - start_time) > 10: - for proc in psutil.process_iter(): - if proc.name() == "Un_A.exe": - proc.kill() - time.sleep(0.1) - - # This is needed to avoid a race condition where the installer isn't closed - start_time = time.time() - while os.path.basename(INST_BIN) in (p.name() for p in psutil.process_iter()): - if (time.time() - start_time) > 10: - # If it's not dead after 10 seconds, kill it - for proc in psutil.process_iter(): - if proc.name() == os.path.basename(INST_BIN): - proc.kill() - time.sleep(0.1) - +def clean_fragments(inst_dir=INST_DIR): # Remove root_dir if os.path.exists(DATA_DIR): shutil.rmtree(DATA_DIR) + assert not os.path.exists(DATA_DIR) + # Remove install dir if os.path.exists(inst_dir): shutil.rmtree(inst_dir) + assert not os.path.exists(inst_dir) + # Remove old salt dir (C:\salt) if os.path.exists(OLD_DIR): shutil.rmtree(OLD_DIR) + assert not os.path.exists(OLD_DIR) + # Remove custom config - if os.path.exists(rf"{REPO_DIR}\custom_conf"): - os.remove(rf"{REPO_DIR}\custom_conf") + if os.path.exists(rf"{SCRIPT_DIR}\custom_conf"): + os.remove(rf"{SCRIPT_DIR}\custom_conf") + assert not os.path.exists(rf"{SCRIPT_DIR}\custom_conf") + # Remove registry entries delete_key(key="SOFTWARE\\Salt Project\\Salt") + assert not reg_key_exists( + hive=winreg.HKEY_LOCAL_MACHINE, key="SOFTWARE\\Salt Project\\Salt" + ) + delete_key(key="SOFTWARE\\Salt Project") + assert not reg_key_exists( + hive=winreg.HKEY_LOCAL_MACHINE, key="SOFTWARE\\Salt Project" + ) + + return True + + +@pytest.helpers.register +def clean_env(inst_dir=INST_DIR, timeout=300): + # Let's make sure none of the install/uninstall processes are running + for proc in PROCESSES: + try: + assert proc not in (p.name() for p in psutil.process_iter()) + except psutil.NoSuchProcess: + continue + + # Uninstall existing installation + # Run the uninstaller. + for uninst_bin in [f"{inst_dir}\\uninst.exe", f"{OLD_DIR}\\uninst.exe"]: + if os.path.exists(uninst_bin): + install_dir = os.path.dirname(uninst_bin) + cmd = [f'"{uninst_bin}"', "/S", "/delete-root-dir", "/delete-install-dir"] + run_command(cmd) + + # Uninst.exe launches a 2nd binary (Un.exe or Un_*.exe) + # Let's get the name of the process + proc_name = "" + for proc in PROCESSES: + try: + if proc in (p.name() for p in psutil.process_iter()): + proc_name = proc + except psutil.NoSuchProcess: + continue + + # We need to give the process time to exit + if proc_name: + elapsed_time = 0 + while elapsed_time < timeout: + try: + if proc_name not in (p.name() for p in psutil.process_iter()): + break + except psutil.NoSuchProcess: + continue + elapsed_time += 0.1 + time.sleep(0.1) + + assert clean_fragments(inst_dir=install_dir) + + return True @pytest.helpers.register @@ -134,12 +189,15 @@ def existing_config(): @pytest.helpers.register def custom_config(): - if os.path.exists(rf"{REPO_DIR}\custom_conf"): - os.remove(rf"{REPO_DIR}\custom_conf") + conf_file = rf"{SCRIPT_DIR}\custom_conf" + if os.path.exists(conf_file): + os.remove(conf_file) # Create a custom config - with open(rf"{REPO_DIR}\custom_conf", "w") as f: + with open(conf_file, "w") as f: # \n characters are converted to os.linesep f.writelines(custom_content) + assert os.path.exists(conf_file) + return conf_file @pytest.helpers.register @@ -158,25 +216,78 @@ def old_install(): with open(f"{OLD_DIR}\\conf\\minion", "w") as f: # \n characters are converted to os.linesep f.writelines(old_content) - while not (os.path.exists(f"{OLD_DIR}\\bin\\python.exe")): - time.sleep(0.1) - while not (os.path.exists(f"{OLD_DIR}\\bin\\ssm.exe")): - time.sleep(0.1) - while not (os.path.exists(f"{OLD_DIR}\\conf\\minion")): - time.sleep(0.1) + assert os.path.exists(f"{OLD_DIR}\\bin\\python.exe") assert os.path.exists(f"{OLD_DIR}\\bin\\ssm.exe") assert os.path.exists(f"{OLD_DIR}\\conf\\minion") @pytest.helpers.register -def run_command(cmd): - result = subprocess.run(cmd, capture_output=True, text=True) - return result.stdout.strip().replace("/", "\\") +def install_salt(args): + """ + Cleans the environment and installs salt with passed arguments + """ + cmd = [f'"{INST_BIN}"'] + if isinstance(args, str): + cmd.append(args) + elif isinstance(args, list): + cmd.extend(args) + else: + raise TypeError(f"Invalid args format: {args}") + run_command(cmd) + + # Let's make sure none of the install/uninstall processes are running + try: + assert os.path.basename(INST_BIN) not in ( + p.name() for p in psutil.process_iter() + ) + except psutil.NoSuchProcess: + pass -# These are at the bottom because they depend on some of the functions -REPO_DIR = run_command(["git", "rev-parse", "--show-toplevel"]) -REPO_DIR = rf"{REPO_DIR}\pkg\windows\nsis\tests" -os.chdir(REPO_DIR) -INST_BIN = rf"{REPO_DIR}\test-setup.exe" +def is_file_locked(path): + """ + Try to see if a file is locked + """ + if not (os.path.exists(path)): + return False + try: + f = open(path) + f.close() + except OSError: + return True + return False + + +@pytest.helpers.register +def run_command(cmd_args, timeout=300): + if isinstance(cmd_args, list): + cmd_args = " ".join(cmd_args) + + bin_file = re.findall(r'"(.*?)"', cmd_args)[0] + + elapsed_time = 0 + while ( + os.path.exists(bin_file) and is_file_locked(bin_file) and elapsed_time < timeout + ): + elapsed_time += 0.1 + time.sleep(0.1) + + proc = subprocess.Popen(cmd_args, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + + elapsed_time = 0 + while ( + os.path.exists(bin_file) and is_file_locked(bin_file) and elapsed_time < timeout + ): + elapsed_time += 0.1 + time.sleep(0.1) + + try: + out, err = proc.communicate(timeout=timeout) + assert proc.returncode == 0 + except subprocess.TimeoutExpired: + # This hides the hung installer/uninstaller problem + proc.kill() + out = "process killed" + + return out diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_custom_full_path.py b/pkg/windows/nsis/tests/manual_tests/test_manual_custom_full_path.py index 6c543c1ccf9..1c2c160651f 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_custom_full_path.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_custom_full_path.py @@ -6,14 +6,12 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create a custom config - pytest.helpers.custom_config() - - full_path_conf = f"{pytest.REPO_DIR}\\custom_conf" - - pytest.helpers.run_command([pytest.INST_BIN, f"/custom-config={full_path_conf}"]) - yield + full_path_conf = pytest.helpers.custom_config() + # Install salt passing custom-config + args = [f"/custom-config={full_path_conf}"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() @@ -27,7 +25,7 @@ def test_config_present(install): def test_config_correct(install): # The config file should be the default, unchanged - with open(f"{pytest.REPO_DIR}\\custom_conf") as f: + with open(f"{pytest.SCRIPT_DIR}\\custom_conf") as f: expected = f.readlines() with open(f"{pytest.DATA_DIR}\\conf\\minion") as f: diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_custom_master.py b/pkg/windows/nsis/tests/manual_tests/test_manual_custom_master.py index e95df53ff11..a93b7b68dbe 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_custom_master.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_custom_master.py @@ -6,14 +6,11 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/custom-config=custom_conf", "/master=cli_master"] - ) - yield + args = ["/custom-config=custom_conf", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_custom_master_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_custom_master_minion.py index b6fbc71d778..5ee86d2bb41 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_custom_master_minion.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_custom_master_minion.py @@ -6,19 +6,15 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/custom-config=custom_conf", - "/master=cli_master", - "/minion-name=cli_minion", - ] - ) - yield + args = [ + "/custom-config=custom_conf", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_custom_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_custom_minion.py index c90bfb159cf..2a91d9fedf0 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_custom_minion.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_custom_minion.py @@ -6,14 +6,11 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/custom-config=custom_conf", "/minion-name=cli_minion"] - ) - yield + args = ["/custom-config=custom_conf", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_custom_rel_path.py b/pkg/windows/nsis/tests/manual_tests/test_manual_custom_rel_path.py index 61458486a43..bbb949dc2d3 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_custom_rel_path.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_custom_rel_path.py @@ -6,12 +6,11 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command([pytest.INST_BIN, "/custom-config=custom_conf"]) - yield + args = ["/custom-config=custom_conf"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() @@ -25,7 +24,7 @@ def test_config_present(install): def test_config_correct(install): # The config file should be the default, unchanged - with open(f"{pytest.REPO_DIR}\\custom_conf") as f: + with open(f"{pytest.SCRIPT_DIR}\\custom_conf") as f: expected = f.readlines() with open(f"{pytest.DATA_DIR}\\conf\\minion") as f: diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_default.py b/pkg/windows/nsis/tests/manual_tests/test_manual_default.py index 3ad8cbde51f..b3cd13cc038 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_default.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_default.py @@ -6,8 +6,9 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - pytest.helpers.run_command([pytest.INST_BIN]) - yield + args = [] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() @@ -21,7 +22,7 @@ def test_config_present(install): def test_config_correct(install): # The config file should be the default, unchanged - with open(f"{pytest.REPO_DIR}\\tests\\_files\\minion") as f: + with open(rf"{pytest.SCRIPT_DIR}\_files\minion") as f: expected = f.readlines() with open(f"{pytest.DATA_DIR}\\conf\\minion") as f: diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_default_master.py b/pkg/windows/nsis/tests/manual_tests/test_manual_default_master.py index 1b819c7db7d..5935981d362 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_default_master.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_default_master.py @@ -6,13 +6,14 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - pytest.helpers.run_command([pytest.INST_BIN, "/master=cli_master"]) - yield + args = ["/master=cli_master"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() def test_binaries_present(install): - assert os.path.exists(f"{pytest.INST_DIR}\\bsm.exe") + assert os.path.exists(f"{pytest.INST_DIR}\\ssm.exe") def test_config_present(install): diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_default_master_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_default_master_minion.py index 101238d4623..acd4d313b4f 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_default_master_minion.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_default_master_minion.py @@ -6,10 +6,9 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - pytest.helpers.run_command( - [pytest.INST_BIN, "/master=cli_master", "/minion-name=cli_minion"] - ) - yield + args = ["/master=cli_master", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_default_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_default_minion.py index 538b6328968..79d9e452dc1 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_default_minion.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_default_minion.py @@ -6,8 +6,9 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - pytest.helpers.run_command([pytest.INST_BIN, "/minion-name=cli_minion"]) - yield + args = ["/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_existing.py b/pkg/windows/nsis/tests/manual_tests/test_manual_existing.py index 926637081c0..a150adb48a8 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_existing.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_existing.py @@ -6,12 +6,11 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - - pytest.helpers.run_command([pytest.INST_BIN]) - yield + args = [] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom.py b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom.py index 6d164e98a9c..af6292cf282 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom.py @@ -6,15 +6,13 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command([pytest.INST_BIN, "/custom-config=custom_conf"]) - yield + args = ["/custom-config=custom_conf"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() @@ -28,7 +26,7 @@ def test_config_present(install): def test_config_correct(install): # The config file should be the default, unchanged - with open(f"{pytest.REPO_DIR}\\custom_conf") as f: + with open(f"{pytest.SCRIPT_DIR}\\custom_conf") as f: expected = f.readlines() with open(f"{pytest.DATA_DIR}\\conf\\minion") as f: diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom_master.py b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom_master.py index 26d182ca95b..252c5801ff4 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom_master.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom_master.py @@ -6,17 +6,13 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/custom-config=custom_conf", "/master=cli_master"] - ) - yield + args = ["/custom-config=custom_conf", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom_master_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom_master_minion.py index 2c8ac4aafee..f2e079f7439 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom_master_minion.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom_master_minion.py @@ -6,22 +6,17 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/custom-config=custom_conf", - "/master=cli_master", - "/minion-name=cli_minion", - ] - ) - yield + args = [ + "/custom-config=custom_conf", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom_minion.py index 092857b39b9..b31ad3db90b 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom_minion.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_custom_minion.py @@ -8,14 +8,11 @@ def install(): pytest.helpers.clean_env() # Create an existing config pytest.helpers.existing_config() - # Create a custom config pytest.helpers.custom_config() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/custom-config=custom_conf", "/minion-name=cli_minion"] - ) - yield + args = ["/custom-config=custom_conf", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default.py b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default.py index 1007fcb97d4..515b835394c 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default.py @@ -6,12 +6,11 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - - pytest.helpers.run_command([pytest.INST_BIN, "/default-config"]) - yield + args = ["/default-config"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() @@ -25,7 +24,7 @@ def test_config_present(install): def test_config_correct(install): # The config file should be the default, unchanged - with open(f"{pytest.REPO_DIR}\\tests\\_files\\minion") as f: + with open(rf"{pytest.SCRIPT_DIR}\_files\minion") as f: expected = f.readlines() with open(f"{pytest.DATA_DIR}\\conf\\minion") as f: diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default_master.py b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default_master.py index 81d66801cb5..f054cbfbe9c 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default_master.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default_master.py @@ -6,14 +6,11 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/default-config", "/master=cli_master"] - ) - yield + args = ["/default-config", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() diff --git a/pkg/windows/nsis/tests/manual_tests/test_manulal_existing_default_master_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default_master_minion.py similarity index 83% rename from pkg/windows/nsis/tests/manual_tests/test_manulal_existing_default_master_minion.py rename to pkg/windows/nsis/tests/manual_tests/test_manual_existing_default_master_minion.py index 65c7dc8b0df..ee5703740c5 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manulal_existing_default_master_minion.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default_master_minion.py @@ -6,19 +6,11 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - - pytest.helpers.run_command( - [ - pytest.INST_BIN, - "/default-config", - "/master=cli_master", - "/minion-name=cli_minion", - ] - ) - yield + args = ["/default-config", "/master=cli_master", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default_minion.py index 09db2177d5f..a5d0f4f6f3b 100644 --- a/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default_minion.py +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_existing_default_minion.py @@ -6,14 +6,11 @@ import pytest @pytest.fixture(scope="module") def install(): pytest.helpers.clean_env() - # Create an existing config pytest.helpers.existing_config() - - pytest.helpers.run_command( - [pytest.INST_BIN, "/default-config", "/minion-name=cli_minion"] - ) - yield + args = ["/default-config", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args pytest.helpers.clean_env() diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_custom.py b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_custom.py new file mode 100644 index 00000000000..cd8213b9f39 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_custom.py @@ -0,0 +1,41 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def inst_dir(): + return "C:\\custom_location" + + +@pytest.fixture(scope="module") +def install(inst_dir): + pytest.helpers.clean_env() + # Create a custom config + pytest.helpers.custom_config() + args = [f"/install-dir={inst_dir}", "/custom-config=custom_conf"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env(inst_dir) + + +def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") + + +def test_config_present(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the custom config, unchanged + with open(rf"{pytest.SCRIPT_DIR}\custom_conf") as f: + expected = f.readlines() + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_custom_master.py b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_custom_master.py new file mode 100644 index 00000000000..1ddbd4948af --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_custom_master.py @@ -0,0 +1,53 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def inst_dir(): + return "C:\\custom_location" + + +@pytest.fixture(scope="module") +def install(inst_dir): + pytest.helpers.clean_env() + # Create a custom config + pytest.helpers.custom_config() + args = [ + f"/install-dir={inst_dir}", + "/custom-config=custom_conf", + "/master=cli_master", + ] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env(inst_dir) + + +def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") + + +def test_config_present(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the custom config with only master set + expected = [ + "# Custom config from test suite line 1/6\n", + "master: cli_master\n", + "# Custom config from test suite line 2/6\n", + "id: custom_minion\n", + "# Custom config from test suite line 3/6\n", + "# Custom config from test suite line 4/6\n", + "# Custom config from test suite line 5/6\n", + "# Custom config from test suite line 6/6\n", + ] + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_custom_master_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_custom_master_minion.py new file mode 100644 index 00000000000..eadc478ad40 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_custom_master_minion.py @@ -0,0 +1,54 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def inst_dir(): + return "C:\\custom_location" + + +@pytest.fixture(scope="module") +def install(inst_dir): + pytest.helpers.clean_env() + # Create a custom config + pytest.helpers.custom_config() + args = [ + f"/install-dir={inst_dir}", + "/custom-config=custom_conf", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env(inst_dir) + + +def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") + + +def test_config_present(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the custom config with master and minion set + expected = [ + "# Custom config from test suite line 1/6\n", + "master: cli_master\n", + "# Custom config from test suite line 2/6\n", + "id: cli_minion\n", + "# Custom config from test suite line 3/6\n", + "# Custom config from test suite line 4/6\n", + "# Custom config from test suite line 5/6\n", + "# Custom config from test suite line 6/6\n", + ] + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_custom_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_custom_minion.py new file mode 100644 index 00000000000..f896ed8c63c --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_custom_minion.py @@ -0,0 +1,53 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def inst_dir(): + return "C:\\custom_location" + + +@pytest.fixture(scope="module") +def install(inst_dir): + pytest.helpers.clean_env() + # Create a custom config + pytest.helpers.custom_config() + args = [ + f"/install-dir={inst_dir}", + "/custom-config=custom_conf", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env(inst_dir) + + +def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") + + +def test_config_present(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the custom config with only minion set + expected = [ + "# Custom config from test suite line 1/6\n", + "master: custom_master\n", + "# Custom config from test suite line 2/6\n", + "id: cli_minion\n", + "# Custom config from test suite line 3/6\n", + "# Custom config from test suite line 4/6\n", + "# Custom config from test suite line 5/6\n", + "# Custom config from test suite line 6/6\n", + ] + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_default.py b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_default.py new file mode 100644 index 00000000000..c42bb50e02e --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_default.py @@ -0,0 +1,39 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def inst_dir(): + return "C:\\custom_location" + + +@pytest.fixture(scope="module") +def install(inst_dir): + pytest.helpers.clean_env() + args = [f"/install-dir={inst_dir}"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env(inst_dir) + + +def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") + + +def test_config_present(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the default config, unchanged + with open(rf"{pytest.SCRIPT_DIR}\_files\minion") as f: + expected = f.readlines() + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_default_master.py b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_default_master.py new file mode 100644 index 00000000000..291d110f8d9 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_default_master.py @@ -0,0 +1,47 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def inst_dir(): + return "C:\\custom_location" + + +@pytest.fixture(scope="module") +def install(inst_dir): + pytest.helpers.clean_env() + args = [f"/install-dir={inst_dir}", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env(inst_dir) + + +def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") + + +def test_config_present(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the default config with only master set + expected = [ + "# Default config from test suite line 1/6\n", + "master: cli_master\n", + "# Default config from test suite line 2/6\n", + "#id:\n", + "# Default config from test suite line 3/6\n", + "# Default config from test suite line 4/6\n", + "# Default config from test suite line 5/6\n", + "# Default config from test suite line 6/6\n", + ] + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_default_master_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_default_master_minion.py new file mode 100644 index 00000000000..44c364b9615 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_default_master_minion.py @@ -0,0 +1,47 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def inst_dir(): + return "C:\\custom_location" + + +@pytest.fixture(scope="module") +def install(inst_dir): + pytest.helpers.clean_env() + args = [f"/install-dir={inst_dir}", "/master=cli_master", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env(inst_dir) + + +def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") + + +def test_config_present(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the default config with master and minion set + expected = [ + "# Default config from test suite line 1/6\n", + "master: cli_master\n", + "# Default config from test suite line 2/6\n", + "id: cli_minion\n", + "# Default config from test suite line 3/6\n", + "# Default config from test suite line 4/6\n", + "# Default config from test suite line 5/6\n", + "# Default config from test suite line 6/6\n", + ] + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_default_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_default_minion.py new file mode 100644 index 00000000000..b964852d9dc --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_default_minion.py @@ -0,0 +1,47 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def inst_dir(): + return "C:\\custom_location" + + +@pytest.fixture(scope="module") +def install(inst_dir): + pytest.helpers.clean_env() + args = [f"/install-dir={inst_dir}", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env(inst_dir) + + +def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") + + +def test_config_present(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the default config with just the minion set + expected = [ + "# Default config from test suite line 1/6\n", + "#master: salt\n", + "# Default config from test suite line 2/6\n", + "id: cli_minion\n", + "# Default config from test suite line 3/6\n", + "# Default config from test suite line 4/6\n", + "# Default config from test suite line 5/6\n", + "# Default config from test suite line 6/6\n", + ] + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_existing.py b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_existing.py new file mode 100644 index 00000000000..4f61c24eae3 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_existing.py @@ -0,0 +1,40 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def inst_dir(): + return "C:\\custom_location" + + +@pytest.fixture(scope="module") +def install(inst_dir): + pytest.helpers.clean_env() + # Create an existing config + pytest.helpers.existing_config() + args = [f"/install-dir={inst_dir}"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env(inst_dir) + + +def test_binaries_present(install, inst_dir): + # This will show the contents of the directory on failure + inst_dir_exists = os.path.exists(inst_dir) + dir_contents = os.listdir(inst_dir) + assert os.path.exists(rf"{inst_dir}\ssm.exe") + + +def test_config_present(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the existing config, unchanged + expected = pytest.EXISTING_CONTENT + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_move_old_install.py b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_move_old_install.py new file mode 100644 index 00000000000..0ccd54aab99 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_install_dir_move_old_install.py @@ -0,0 +1,42 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def inst_dir(): + return "C:\\custom_location" + + +@pytest.fixture(scope="module") +def install(inst_dir): + pytest.helpers.clean_env() + # Create old install + pytest.helpers.old_install() + args = [f"/install-dir={inst_dir}", "/move-config"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the existing config in the new location, unchanged + expected = pytest.OLD_CONTENT + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install.py new file mode 100644 index 00000000000..6afcb0af507 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install.py @@ -0,0 +1,37 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + args = [] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_old_location(install): + assert os.path.exists(rf"{pytest.OLD_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the old existing config, unchanged + expected = pytest.OLD_CONTENT + + with open(rf"{pytest.OLD_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_custom.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_custom.py new file mode 100644 index 00000000000..037a4fa2b44 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_custom.py @@ -0,0 +1,40 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + # Create a custom config + pytest.helpers.custom_config() + args = ["/custom-config=custom_conf"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_old_location(install): + assert os.path.exists(rf"{pytest.OLD_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the custom config, unchanged + with open(rf"{pytest.SCRIPT_DIR}\custom_conf") as f: + expected = f.readlines() + + with open(rf"{pytest.OLD_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_custom_master.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_custom_master.py new file mode 100644 index 00000000000..765d378307e --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_custom_master.py @@ -0,0 +1,48 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + # Create a custom config + pytest.helpers.custom_config() + args = ["/custom-config=custom_conf", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_old_location(install): + assert os.path.exists(rf"{pytest.OLD_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the custom config with only master set + expected = [ + "# Custom config from test suite line 1/6\n", + "master: cli_master\n", + "# Custom config from test suite line 2/6\n", + "id: custom_minion\n", + "# Custom config from test suite line 3/6\n", + "# Custom config from test suite line 4/6\n", + "# Custom config from test suite line 5/6\n", + "# Custom config from test suite line 6/6\n", + ] + + with open(rf"{pytest.OLD_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_custom_master_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_custom_master_minion.py new file mode 100644 index 00000000000..b247fd33277 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_custom_master_minion.py @@ -0,0 +1,52 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + # Create a custom config + pytest.helpers.custom_config() + args = [ + "/custom-config=custom_conf", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_old_location(install): + assert os.path.exists(rf"{pytest.OLD_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the custom config with master and minion set + expected = [ + "# Custom config from test suite line 1/6\n", + "master: cli_master\n", + "# Custom config from test suite line 2/6\n", + "id: cli_minion\n", + "# Custom config from test suite line 3/6\n", + "# Custom config from test suite line 4/6\n", + "# Custom config from test suite line 5/6\n", + "# Custom config from test suite line 6/6\n", + ] + + with open(rf"{pytest.OLD_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_custom_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_custom_minion.py new file mode 100644 index 00000000000..23fce833cea --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_custom_minion.py @@ -0,0 +1,48 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + # Create a custom config + pytest.helpers.custom_config() + args = ["/custom-config=custom_conf", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_old_location(install): + assert os.path.exists(rf"{pytest.OLD_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the custom config with only minion set + expected = [ + "# Custom config from test suite line 1/6\n", + "master: custom_master\n", + "# Custom config from test suite line 2/6\n", + "id: cli_minion\n", + "# Custom config from test suite line 3/6\n", + "# Custom config from test suite line 4/6\n", + "# Custom config from test suite line 5/6\n", + "# Custom config from test suite line 6/6\n", + ] + + with open(rf"{pytest.OLD_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_default.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_default.py new file mode 100644 index 00000000000..0f782538e3a --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_default.py @@ -0,0 +1,38 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old install + pytest.helpers.old_install() + args = ["/default-config"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_old_location(install): + assert os.path.exists(rf"{pytest.OLD_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the default config, unchanged + with open(rf"{pytest.SCRIPT_DIR}\_files\minion") as f: + expected = f.readlines() + + with open(rf"{pytest.OLD_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_default_master.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_default_master.py new file mode 100644 index 00000000000..77085dc6403 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_default_master.py @@ -0,0 +1,46 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + args = ["/default-config", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_old_location(install): + assert os.path.exists(rf"{pytest.OLD_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the default config with only master set + expected = [ + "# Default config from test suite line 1/6\n", + "master: cli_master\n", + "# Default config from test suite line 2/6\n", + "#id:\n", + "# Default config from test suite line 3/6\n", + "# Default config from test suite line 4/6\n", + "# Default config from test suite line 5/6\n", + "# Default config from test suite line 6/6\n", + ] + + with open(rf"{pytest.OLD_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_default_master_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_default_master_minion.py new file mode 100644 index 00000000000..0b00cb6c992 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_default_master_minion.py @@ -0,0 +1,50 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + args = [ + "/default-config", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_old_location(install): + assert os.path.exists(rf"{pytest.OLD_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the default config with master and minion set + expected = [ + "# Default config from test suite line 1/6\n", + "master: cli_master\n", + "# Default config from test suite line 2/6\n", + "id: cli_minion\n", + "# Default config from test suite line 3/6\n", + "# Default config from test suite line 4/6\n", + "# Default config from test suite line 5/6\n", + "# Default config from test suite line 6/6\n", + ] + + with open(rf"{pytest.OLD_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_default_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_default_minion.py new file mode 100644 index 00000000000..db4d10b1c0c --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_default_minion.py @@ -0,0 +1,46 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + args = ["/default-config", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_old_location(install): + assert os.path.exists(rf"{pytest.OLD_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the default with only minion set + expected = [ + "# Default config from test suite line 1/6\n", + "#master: salt\n", + "# Default config from test suite line 2/6\n", + "id: cli_minion\n", + "# Default config from test suite line 3/6\n", + "# Default config from test suite line 4/6\n", + "# Default config from test suite line 5/6\n", + "# Default config from test suite line 6/6\n", + ] + + with open(rf"{pytest.OLD_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move.py new file mode 100644 index 00000000000..cfa48e0b716 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move.py @@ -0,0 +1,37 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + args = ["/move-config"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_new_location(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the old existing config in the new location, unchanged + expected = pytest.OLD_CONTENT + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_custom.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_custom.py new file mode 100644 index 00000000000..b711f78eace --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_custom.py @@ -0,0 +1,40 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + # Create a custom config + pytest.helpers.custom_config() + args = ["/custom-config=custom_conf", "/move-config"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_new_location(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the custom config in the new location, unchanged + with open(rf"{pytest.SCRIPT_DIR}\custom_conf") as f: + expected = f.readlines() + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_custom_master.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_custom_master.py new file mode 100644 index 00000000000..cd2410d311d --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_custom_master.py @@ -0,0 +1,48 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + # Create a custom config + pytest.helpers.custom_config() + args = ["/custom-config=custom_conf", "/move-config", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_new_location(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the custom config in the new location with only master set + expected = [ + "# Custom config from test suite line 1/6\n", + "master: cli_master\n", + "# Custom config from test suite line 2/6\n", + "id: custom_minion\n", + "# Custom config from test suite line 3/6\n", + "# Custom config from test suite line 4/6\n", + "# Custom config from test suite line 5/6\n", + "# Custom config from test suite line 6/6\n", + ] + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_custom_master_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_custom_master_minion.py new file mode 100644 index 00000000000..32deccfe4cd --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_custom_master_minion.py @@ -0,0 +1,53 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + # Create a custom config + pytest.helpers.custom_config() + args = [ + "/custom-config=custom_conf", + "/move-config", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_new_location(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the custom config in the new location with master and minion set + expected = [ + "# Custom config from test suite line 1/6\n", + "master: cli_master\n", + "# Custom config from test suite line 2/6\n", + "id: cli_minion\n", + "# Custom config from test suite line 3/6\n", + "# Custom config from test suite line 4/6\n", + "# Custom config from test suite line 5/6\n", + "# Custom config from test suite line 6/6\n", + ] + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_custom_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_custom_minion.py new file mode 100644 index 00000000000..6ff619ed9a5 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_custom_minion.py @@ -0,0 +1,48 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + # Create a custom config + pytest.helpers.custom_config() + args = ["/custom-config=custom_conf", "/move-config", "/minion-name=cli_minion"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_new_location(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the custom config in the new location with only minion set + expected = [ + "# Custom config from test suite line 1/6\n", + "master: custom_master\n", + "# Custom config from test suite line 2/6\n", + "id: cli_minion\n", + "# Custom config from test suite line 3/6\n", + "# Custom config from test suite line 4/6\n", + "# Custom config from test suite line 5/6\n", + "# Custom config from test suite line 6/6\n", + ] + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_default.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_default.py new file mode 100644 index 00000000000..5a64d7e4b28 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_default.py @@ -0,0 +1,38 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + args = ["/move-config", "/default-config"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_new_location(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the default config in the new location, unchanged + with open(rf"{pytest.SCRIPT_DIR}\_files\minion") as f: + expected = f.readlines() + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_default_master.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_default_master.py new file mode 100644 index 00000000000..bd37b2565fe --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_default_master.py @@ -0,0 +1,46 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + args = ["/default-config", "/move-config", "/master=cli_master"] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_new_location(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the default config in the new location with only master set + expected = [ + "# Default config from test suite line 1/6\n", + "master: cli_master\n", + "# Default config from test suite line 2/6\n", + "#id:\n", + "# Default config from test suite line 3/6\n", + "# Default config from test suite line 4/6\n", + "# Default config from test suite line 5/6\n", + "# Default config from test suite line 6/6\n", + ] + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_default_master_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_default_master_minion.py new file mode 100644 index 00000000000..bc8413aea16 --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_default_master_minion.py @@ -0,0 +1,51 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + args = [ + "/default-config", + "/move-config", + "/master=cli_master", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_new_location(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the default config in the new location with master and minion set + expected = [ + "# Default config from test suite line 1/6\n", + "master: cli_master\n", + "# Default config from test suite line 2/6\n", + "id: cli_minion\n", + "# Default config from test suite line 3/6\n", + "# Default config from test suite line 4/6\n", + "# Default config from test suite line 5/6\n", + "# Default config from test suite line 6/6\n", + ] + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_default_minion.py b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_default_minion.py new file mode 100644 index 00000000000..0d90d00f0df --- /dev/null +++ b/pkg/windows/nsis/tests/manual_tests/test_manual_old_install_move_default_minion.py @@ -0,0 +1,50 @@ +import os + +import pytest + + +@pytest.fixture(scope="module") +def install(): + pytest.helpers.clean_env() + # Create old config + pytest.helpers.old_install() + args = [ + "/default-config", + "/move-config", + "/minion-name=cli_minion", + ] + pytest.helpers.install_salt(args) + yield args + pytest.helpers.clean_env() + + +def test_binaries_present_old_location(install): + # This will show the contents of the directory on failure + dir_contents = os.listdir(rf"{pytest.OLD_DIR}\bin") + # Apparently we don't move the binaries even if they pass install-dir + # TODO: Decide if this is expected behavior + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\ssm.exe") + assert os.path.exists(rf"{pytest.OLD_DIR}\bin\python.exe") + + +def test_config_present_new_location(install): + assert os.path.exists(rf"{pytest.DATA_DIR}\conf\minion") + + +def test_config_correct(install): + # The config file should be the default config in the new location with only minion set + expected = [ + "# Default config from test suite line 1/6\n", + "#master: salt\n", + "# Default config from test suite line 2/6\n", + "id: cli_minion\n", + "# Default config from test suite line 3/6\n", + "# Default config from test suite line 4/6\n", + "# Default config from test suite line 5/6\n", + "# Default config from test suite line 6/6\n", + ] + + with open(rf"{pytest.DATA_DIR}\conf\minion") as f: + result = f.readlines() + + assert result == expected diff --git a/pkg/windows/nsis/tests/pytest.ini b/pkg/windows/nsis/tests/pytest.ini new file mode 100644 index 00000000000..eea2c180278 --- /dev/null +++ b/pkg/windows/nsis/tests/pytest.ini @@ -0,0 +1 @@ +[pytest] diff --git a/pkg/windows/nsis/tests/quick_setup.ps1 b/pkg/windows/nsis/tests/quick_setup.ps1 new file mode 100644 index 00000000000..454acc937c3 --- /dev/null +++ b/pkg/windows/nsis/tests/quick_setup.ps1 @@ -0,0 +1,154 @@ +<# +.SYNOPSIS +Script that sets up the environment for testing + +.DESCRIPTION +This script creates the directory structure and files needed build a mock salt +installer for testing + +.EXAMPLE +setup.ps1 +#> +param( + [Parameter(Mandatory=$false)] + [Alias("c")] +# Don't pretify the output of the Write-Result + [Switch] $CICD +) + +#------------------------------------------------------------------------------- +# Script Preferences +#------------------------------------------------------------------------------- + +$ProgressPreference = "SilentlyContinue" +$ErrorActionPreference = "Stop" + +#------------------------------------------------------------------------------- +# Script Functions +#------------------------------------------------------------------------------- + +function Write-Result($result, $ForegroundColor="Green") { + if ( $CICD ) { + Write-Host $result -ForegroundColor $ForegroundColor + } else { + $position = 80 - $result.Length - [System.Console]::CursorLeft + Write-Host -ForegroundColor $ForegroundColor ("{0,$position}$result" -f "") + } +} + +#------------------------------------------------------------------------------- +# Script Variables +#------------------------------------------------------------------------------- + +$PROJECT_DIR = $(git rev-parse --show-toplevel) +$SCRIPT_DIR = (Get-ChildItem "$($myInvocation.MyCommand.Definition)").DirectoryName +$WINDOWS_DIR = "$PROJECT_DIR\pkg\windows" +$NSIS_DIR = "$WINDOWS_DIR\nsis" +$BUILDENV_DIR = "$WINDOWS_DIR\buildenv" +$NSIS_BIN = "$( ${env:ProgramFiles(x86)} )\NSIS\makensis.exe" +$SALT_DEP_URL = "https://repo.saltproject.io/windows/dependencies/64" + +#------------------------------------------------------------------------------- +# Script Start +#------------------------------------------------------------------------------- + +Write-Host $("=" * 80) +Write-Host "Build Test Environment for NSIS Tests" -ForegroundColor Cyan +Write-Host $("-" * 80) + +#------------------------------------------------------------------------------- +# Setup Directories +#------------------------------------------------------------------------------- + +$directories = "$BUILDENV_DIR", + "$BUILDENV_DIR\configs" +$directories | ForEach-Object { + if ( ! (Test-Path -Path "$_") ) { + Write-Host "Creating $_`: " -NoNewline + New-Item -Path $_ -ItemType Directory | Out-Null + if ( Test-Path -Path "$_" ) { + Write-Result "Success" + } else { + Write-Result "Failed" -ForegroundColor Red + exit 1 + } + } +} + +#------------------------------------------------------------------------------- +# Create binaries +#------------------------------------------------------------------------------- + +$binary_files = @("python.exe") +$binary_files | ForEach-Object { + Write-Host "Creating $_`: " -NoNewline + Set-Content -Path "$BUILDENV_DIR\$_" -Value "binary" + if ( Test-Path -Path "$BUILDENV_DIR\$_" ) { + Write-Result "Success" + } else { + Write-Result "Failed" -ForegroundColor Red + exit 1 + } +} + +# Make sure ssm.exe is present. This is needed for VMtools +if ( ! (Test-Path -Path "$BUILDENV_DIR\ssm.exe") ) { + Write-Host "Copying SSM to Build Env: " -NoNewline + Invoke-WebRequest -Uri "$SALT_DEP_URL/ssm-2.24-103-gdee49fc.exe" -OutFile "$BUILDENV_DIR\ssm.exe" + if ( Test-Path -Path "$BUILDENV_DIR\ssm.exe" ) { + Write-Result "Success" -ForegroundColor Green + } else { + Write-Result "Failed" -ForegroundColor Red + exit 1 + } +} + +#------------------------------------------------------------------------------- +# Copy Configs +#------------------------------------------------------------------------------- + +Write-Host "Copy testing minion config: " -NoNewline +Copy-Item -Path "$NSIS_DIR\tests\_files\minion" ` + -Destination "$BUILDENV_DIR\configs\" +if ( Test-Path -Path "$BUILDENV_DIR\configs\minion" ) { + Write-Result "Success" +} else { + Write-Result "Failed" -ForegroundColor Red + exit 1 +} + +#------------------------------------------------------------------------------- +# Build mock installer +#------------------------------------------------------------------------------- +Write-Host "Building mock installer: " -NoNewline +Start-Process -FilePath $NSIS_BIN ` + -ArgumentList "/DSaltVersion=test", ` + "/DPythonArchitecture=AMD64", ` + "$NSIS_DIR\installer\Salt-Minion-Setup.nsi" ` + -Wait -WindowStyle Hidden +$installer = "$NSIS_DIR\installer\Salt-Minion-test-Py3-AMD64-Setup.exe" +if ( Test-Path -Path "$installer" ) { + Write-Result "Success" +} else { + Write-Result "Failed" -ForegroundColor Red + Write-Host "$NSIS_BIN /DSaltVersion=test /DPythonArchitecture=AMD64 $NSIS_DIR\installer\Salt-Minion-Setup.nsi" + exit 1 +} + +Write-Host "Moving mock installer: " -NoNewline +$test_installer = "$NSIS_DIR\tests\test-setup.exe" +Move-Item -Path $installer -Destination "$test_installer" -Force +if ( Test-Path -Path "$test_installer" ) { + Write-Result "Success" +} else { + Write-Result "Failed" -ForegroundColor Red + exit 1 +} + +#------------------------------------------------------------------------------- +# Script Complete +#------------------------------------------------------------------------------- + +Write-Host $("-" * 80) +Write-Host "Build Test Environment for NSIS Tests Complete" -ForegroundColor Cyan +Write-Host $("=" * 80) diff --git a/pkg/windows/nsis/tests/setup.ps1 b/pkg/windows/nsis/tests/setup.ps1 index 37ca0f74640..7f23b6f65f1 100644 --- a/pkg/windows/nsis/tests/setup.ps1 +++ b/pkg/windows/nsis/tests/setup.ps1 @@ -9,6 +9,12 @@ installer for testing .EXAMPLE setup.ps1 #> +param( + [Parameter(Mandatory=$false)] + [Alias("c")] +# Don't pretify the output of the Write-Result + [Switch] $CICD +) #------------------------------------------------------------------------------- # Script Preferences @@ -22,8 +28,12 @@ $ErrorActionPreference = "Stop" #------------------------------------------------------------------------------- function Write-Result($result, $ForegroundColor="Green") { - $position = 80 - $result.Length - [System.Console]::CursorLeft - Write-Host -ForegroundColor $ForegroundColor ("{0,$position}$result" -f "") + if ( $CICD ) { + Write-Host $result -ForegroundColor $ForegroundColor + } else { + $position = 80 - $result.Length - [System.Console]::CursorLeft + Write-Host -ForegroundColor $ForegroundColor ("{0,$position}$result" -f "") + } } #------------------------------------------------------------------------------- @@ -37,6 +47,7 @@ $NSIS_DIR = "$WINDOWS_DIR\nsis" $BUILDENV_DIR = "$WINDOWS_DIR\buildenv" $PREREQS_DIR = "$WINDOWS_DIR\prereqs" $NSIS_BIN = "$( ${env:ProgramFiles(x86)} )\NSIS\makensis.exe" +$SALT_DEP_URL = "https://repo.saltproject.io/windows/dependencies/64" #------------------------------------------------------------------------------- # Script Start @@ -83,8 +94,8 @@ $prereq_files | ForEach-Object { } } -$binary_files = "ssm.exe", - "python.exe" +$binary_files = @("python.exe", "ssm.exe") + $binary_files | ForEach-Object { Write-Host "Creating $_`: " -NoNewline Set-Content -Path "$BUILDENV_DIR\$_" -Value "binary" @@ -96,11 +107,23 @@ $binary_files | ForEach-Object { } } +# Make sure ssm.exe is present. This is needed for VMtools +if ( ! (Test-Path -Path "$BUILDENV_DIR\ssm.exe") ) { + Write-Host "Copying SSM to Build Env: " -NoNewline + Invoke-WebRequest -Uri "$SALT_DEP_URL/ssm-2.24-103-gdee49fc.exe" -OutFile "$BUILDENV_DIR\ssm.exe" + if ( Test-Path -Path "$BUILDENV_DIR\ssm.exe" ) { + Write-Result "Success" -ForegroundColor Green + } else { + Write-Result "Failed" -ForegroundColor Red + exit 1 + } +} + #------------------------------------------------------------------------------- # Copy Configs #------------------------------------------------------------------------------- -Write-Host "Copy minion config: " -NoNewline +Write-Host "Copy testing minion config: " -NoNewline Copy-Item -Path "$NSIS_DIR\tests\_files\minion" ` -Destination "$BUILDENV_DIR\configs\" if ( Test-Path -Path "$BUILDENV_DIR\configs\minion" ) { @@ -124,6 +147,7 @@ if ( Test-Path -Path "$installer" ) { Write-Result "Success" } else { Write-Result "Failed" -ForegroundColor Red + Write-Host "$NSIS_BIN /DSaltVersion=test /DPythonArchitecture=AMD64 $NSIS_DIR\installer\Salt-Minion-Setup.nsi" exit 1 } @@ -142,7 +166,7 @@ if ( Test-Path -Path "$test_installer" ) { #------------------------------------------------------------------------------- Write-Host "Setting up venv: " -NoNewline -python.exe -m venv venv +python.exe -m venv "$SCRIPT_DIR\venv" if ( Test-Path -Path "$SCRIPT_DIR\venv" ) { Write-Result "Success" } else { @@ -151,7 +175,7 @@ if ( Test-Path -Path "$SCRIPT_DIR\venv" ) { } Write-Host "Activating venv: " -NoNewline -.\venv\Scripts\activate +& $SCRIPT_DIR\venv\Scripts\activate.ps1 if ( "$env:VIRTUAL_ENV" ) { Write-Result "Success" } else { diff --git a/pkg/windows/nsis/tests/stress_tests/test_hang.py b/pkg/windows/nsis/tests/stress_tests/test_hang.py new file mode 100644 index 00000000000..bea2458a362 --- /dev/null +++ b/pkg/windows/nsis/tests/stress_tests/test_hang.py @@ -0,0 +1,26 @@ +import os + +import pytest + + +@pytest.fixture +def install(): + assert pytest.helpers.clean_env() + args = ["/S"] + pytest.helpers.install_salt(args) + yield args + assert pytest.helpers.clean_env() + + +@pytest.mark.parametrize("execution_number", range(100)) +def test_repeatedly_install_uninstall(execution_number, install): + # Make sure the binaries exists. If they don't, the install failed + assert os.path.exists( + f"{pytest.INST_DIR}\\python.exe" + ), "Installation failed. `python.exe` not found" + assert os.path.exists( + f"{pytest.INST_DIR}\\ssm.exe" + ), "Installation failed. `ssm.exe` not found" + assert os.path.exists( + f"{pytest.INST_DIR}\\uninst.exe" + ), "Installation failed. `uninst.exe` not found" diff --git a/pkg/windows/nsis/tests/test.ps1 b/pkg/windows/nsis/tests/test.ps1 index 015c8b8c60e..c386a69acd9 100644 --- a/pkg/windows/nsis/tests/test.ps1 +++ b/pkg/windows/nsis/tests/test.ps1 @@ -8,6 +8,16 @@ This script activates the venv and launches pytest .EXAMPLE test.ps1 #> +param( + [Parameter(Mandatory=$false)] + [Alias("c")] +# Don't pretify the output of the Write-Result + [Switch] $CICD=$false, + + [Parameter(Mandatory=$false, ValueFromRemainingArguments=$true)] +# Don't pretify the output of the Write-Result + [String]$Tests +) #------------------------------------------------------------------------------- # Script Preferences @@ -21,10 +31,20 @@ $ErrorActionPreference = "Stop" #------------------------------------------------------------------------------- function Write-Result($result, $ForegroundColor="Green") { - $position = 80 - $result.Length - [System.Console]::CursorLeft - Write-Host -ForegroundColor $ForegroundColor ("{0,$position}$result" -f "") + if ( $CICD ) { + Write-Host $result -ForegroundColor $ForegroundColor + } else { + $position = 80 - $result.Length - [System.Console]::CursorLeft + Write-Host -ForegroundColor $ForegroundColor ("{0,$position}$result" -f "") + } } +#------------------------------------------------------------------------------- +# Script Variables +#------------------------------------------------------------------------------- + +$SCRIPT_DIR = (Get-ChildItem "$($myInvocation.MyCommand.Definition)").DirectoryName + #------------------------------------------------------------------------------- # Script Start #------------------------------------------------------------------------------- @@ -36,9 +56,13 @@ Write-Host $("-" * 80) #------------------------------------------------------------------------------- # Activating venv #------------------------------------------------------------------------------- +if ( !(Test-Path -Path "$SCRIPT_DIR\venv\Scripts\activate.ps1") ) { + Write-Host "Could not find virtual environment" + Write-Host "You must run setup.cmd before running this script" +} Write-Host "Activating venv: " -NoNewline -.\venv\Scripts\activate +& $SCRIPT_DIR\venv\Scripts\activate.ps1 if ( "$env:VIRTUAL_ENV" ) { Write-Result "Success" } else { @@ -46,9 +70,27 @@ if ( "$env:VIRTUAL_ENV" ) { exit 1 } +Write-Host "Setting working directory: " -NoNewline +Set-Location -Path $SCRIPT_DIR +if ( $(Get-Location).Path -eq $SCRIPT_DIR ) { + Write-Result "Success" +} else { + Write-Result "Failed" -ForegroundColor Red + exit 1 +} + +Write-Host $("-" * 80) +Write-Host "" Write-Host "Running pytest..." Write-Host "" -pytest -vvv -- .\config_tests\ + +if ($Tests) { + $pytest_args = -join $Tests +} else { + $pytest_args = ".\config_tests\" +} + +pytest -vvv -rPx --showlocals -- $pytest_args #------------------------------------------------------------------------------- # Script Complete diff --git a/requirements/base.txt b/requirements/base.txt index e6ab6f051c4..290e7c44f31 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -5,8 +5,10 @@ jmespath msgpack>=1.0.0 PyYAML MarkupSafe -requests>=2.31.0 ; python_version < '3.8' -requests>=2.32.0 ; python_version >= '3.8' +requests<2.32.0 ; python_version < '3.10' +requests>=2.32.3 ; python_version >= '3.10' +certifi==2023.07.22; python_version < '3.10' +certifi>=2024.7.4; python_version >= '3.10' distro>=1.0.1 psutil>=5.0.0 packaging>=21.3 @@ -34,7 +36,7 @@ rpm-vercmp; sys_platform == 'linux' # From old windows.txt requirements file gitpython>=3.1.37; sys_platform == 'win32' lxml>=4.6.3; sys_platform == 'win32' -pymssql>=2.2.1; sys_platform == 'win32' +pymssql>=2.2.11; sys_platform == 'win32' pymysql>=1.0.2; sys_platform == 'win32' pythonnet>=3.0.1; sys_platform == 'win32' pywin32>=305; sys_platform == 'win32' diff --git a/requirements/pytest.txt b/requirements/pytest.txt index ce8b9569125..82dd9138958 100644 --- a/requirements/pytest.txt +++ b/requirements/pytest.txt @@ -13,3 +13,5 @@ flaky more-itertools pyfakefs trustme +pytest-skip-markers >= 1.5.2 ; python_version >= '3.8' +pytest-skip-markers <= 1.5.1 ; python_version < '3.8' diff --git a/requirements/static/ci/common.in b/requirements/static/ci/common.in index 19b0305ba7d..a09628d653d 100644 --- a/requirements/static/ci/common.in +++ b/requirements/static/ci/common.in @@ -8,7 +8,6 @@ apache-libcloud>=1.5.0; sys_platform != 'win32' boto3>=1.21.46 boto>=2.46.0 cassandra-driver>=2.0 -certifi>=2022.12.07 cffi>=1.14.6 cherrypy>=17.4.1 clustershell @@ -35,7 +34,6 @@ pynacl>=1.5.0 pyinotify>=0.9.6; sys_platform != 'win32' and sys_platform != 'darwin' and platform_system != "openbsd" python-etcd>0.4.2 pyvmomi -requests rfc3987 sqlparse>=0.4.4 strict_rfc3339>=0.7 diff --git a/requirements/static/ci/py3.10/cloud.txt b/requirements/static/ci/py3.10/cloud.txt index e439ead8583..3842f01202d 100644 --- a/requirements/static/ci/py3.10/cloud.txt +++ b/requirements/static/ci/py3.10/cloud.txt @@ -8,7 +8,7 @@ apache-libcloud==3.7.0 ; sys_platform != "win32" # via # -c requirements/static/ci/py3.10/linux.txt # -r requirements/static/ci/cloud.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.10/linux.txt # requests @@ -51,7 +51,7 @@ pywinrm==0.4.3 # via -r requirements/static/ci/cloud.in requests-ntlm==1.2.0 # via pywinrm -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.10/linux.txt # apache-libcloud diff --git a/requirements/static/ci/py3.10/darwin.txt b/requirements/static/ci/py3.10/darwin.txt index cbf72ae9d3a..e00e9bc7933 100644 --- a/requirements/static/ci/py3.10/darwin.txt +++ b/requirements/static/ci/py3.10/darwin.txt @@ -60,10 +60,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests certvalidator==0.11.1 @@ -375,8 +375,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -437,11 +438,10 @@ pyzmq==25.1.2 # -c requirements/static/ci/../pkg/py3.10/darwin.txt # -r requirements/zeromq.txt # pytest-salt-factories -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.10/docs.txt b/requirements/static/ci/py3.10/docs.txt index 3eef9acc60d..367514ad7f7 100644 --- a/requirements/static/ci/py3.10/docs.txt +++ b/requirements/static/ci/py3.10/docs.txt @@ -16,7 +16,7 @@ autocommand==2.2.2 # jaraco.text babel==2.12.1 # via sphinx -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.10/linux.txt # requests @@ -119,7 +119,7 @@ pyyaml==6.0.1 # via # -c requirements/static/ci/py3.10/linux.txt # myst-docutils -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.10/linux.txt # sphinx diff --git a/requirements/static/ci/py3.10/freebsd.txt b/requirements/static/ci/py3.10/freebsd.txt index e6a7f7cc26a..e5a1b0d0c8d 100644 --- a/requirements/static/ci/py3.10/freebsd.txt +++ b/requirements/static/ci/py3.10/freebsd.txt @@ -60,10 +60,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests certvalidator==0.11.1 @@ -379,8 +379,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -441,11 +442,10 @@ pyzmq==25.1.2 # -c requirements/static/ci/../pkg/py3.10/freebsd.txt # -r requirements/zeromq.txt # pytest-salt-factories -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.10/lint.txt b/requirements/static/ci/py3.10/lint.txt index 6be00f6c5bc..24381099e68 100644 --- a/requirements/static/ci/py3.10/lint.txt +++ b/requirements/static/ci/py3.10/lint.txt @@ -6,7 +6,7 @@ # astroid==3.1.0 # via pylint -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.10/linux.txt # requests @@ -36,7 +36,7 @@ pylint==3.1.0 # via # -r requirements/static/ci/lint.in # saltpylint -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.10/linux.txt # docker diff --git a/requirements/static/ci/py3.10/linux.txt b/requirements/static/ci/py3.10/linux.txt index 34906f5148c..1ad89914cfb 100644 --- a/requirements/static/ci/py3.10/linux.txt +++ b/requirements/static/ci/py3.10/linux.txt @@ -70,10 +70,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.10/linux.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # httpcore # httpx # kubernetes @@ -413,8 +413,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -485,11 +486,10 @@ redis-py-cluster==2.1.3 # via -r requirements/static/ci/linux.in redis==3.5.3 # via redis-py-cluster -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.10/linux.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.10/windows.txt b/requirements/static/ci/py3.10/windows.txt index 496f104398f..d67314a94be 100644 --- a/requirements/static/ci/py3.10/windows.txt +++ b/requirements/static/ci/py3.10/windows.txt @@ -52,10 +52,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.10/windows.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests cffi==1.16.0 @@ -305,7 +305,7 @@ pyfakefs==5.3.1 # via -r requirements/pytest.txt pygit2==1.13.1 # via -r requirements/static/ci/windows.in -pymssql==2.2.7 ; sys_platform == "win32" +pymssql==2.3.1 ; sys_platform == "win32" # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # -r requirements/base.txt @@ -337,8 +337,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -407,11 +408,10 @@ pyzmq==25.1.2 # pytest-salt-factories requests-ntlm==1.2.0 # via pywinrm -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # docker # etcd3-py # kubernetes diff --git a/requirements/static/ci/py3.11/cloud.txt b/requirements/static/ci/py3.11/cloud.txt index 5045e69e9b5..96dfc7c1a69 100644 --- a/requirements/static/ci/py3.11/cloud.txt +++ b/requirements/static/ci/py3.11/cloud.txt @@ -8,7 +8,7 @@ apache-libcloud==3.7.0 ; sys_platform != "win32" # via # -c requirements/static/ci/py3.11/linux.txt # -r requirements/static/ci/cloud.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.11/linux.txt # requests @@ -51,7 +51,7 @@ pywinrm==0.4.3 # via -r requirements/static/ci/cloud.in requests-ntlm==1.2.0 # via pywinrm -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.11/linux.txt # apache-libcloud diff --git a/requirements/static/ci/py3.11/darwin.txt b/requirements/static/ci/py3.11/darwin.txt index ff4f6b7e032..80db83a9472 100644 --- a/requirements/static/ci/py3.11/darwin.txt +++ b/requirements/static/ci/py3.11/darwin.txt @@ -55,10 +55,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.11/darwin.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests certvalidator==0.11.1 @@ -368,8 +368,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -430,11 +431,10 @@ pyzmq==25.1.2 # -c requirements/static/ci/../pkg/py3.11/darwin.txt # -r requirements/zeromq.txt # pytest-salt-factories -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.11/darwin.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.11/docs.txt b/requirements/static/ci/py3.11/docs.txt index ffa33dbcf7e..bca5bf5fac9 100644 --- a/requirements/static/ci/py3.11/docs.txt +++ b/requirements/static/ci/py3.11/docs.txt @@ -16,7 +16,7 @@ autocommand==2.2.2 # jaraco.text babel==2.12.1 # via sphinx -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.11/linux.txt # requests @@ -119,7 +119,7 @@ pyyaml==6.0.1 # via # -c requirements/static/ci/py3.11/linux.txt # myst-docutils -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.11/linux.txt # sphinx diff --git a/requirements/static/ci/py3.11/freebsd.txt b/requirements/static/ci/py3.11/freebsd.txt index 6e324cc5f4c..b89d11b60d2 100644 --- a/requirements/static/ci/py3.11/freebsd.txt +++ b/requirements/static/ci/py3.11/freebsd.txt @@ -55,10 +55,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.11/freebsd.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests certvalidator==0.11.1 @@ -372,8 +372,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -434,11 +435,10 @@ pyzmq==25.1.2 # -c requirements/static/ci/../pkg/py3.11/freebsd.txt # -r requirements/zeromq.txt # pytest-salt-factories -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.11/freebsd.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.11/lint.txt b/requirements/static/ci/py3.11/lint.txt index 150c35069c6..7de66dc7e8d 100644 --- a/requirements/static/ci/py3.11/lint.txt +++ b/requirements/static/ci/py3.11/lint.txt @@ -6,7 +6,7 @@ # astroid==3.1.0 # via pylint -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.11/linux.txt # requests @@ -36,7 +36,7 @@ pylint==3.1.0 # via # -r requirements/static/ci/lint.in # saltpylint -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.11/linux.txt # docker diff --git a/requirements/static/ci/py3.11/linux.txt b/requirements/static/ci/py3.11/linux.txt index 5a2f15856d0..320b49135b8 100644 --- a/requirements/static/ci/py3.11/linux.txt +++ b/requirements/static/ci/py3.11/linux.txt @@ -65,10 +65,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.11/linux.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # httpcore # httpx # kubernetes @@ -404,8 +404,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -476,11 +477,10 @@ redis-py-cluster==2.1.3 # via -r requirements/static/ci/linux.in redis==3.5.3 # via redis-py-cluster -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.11/linux.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.11/windows.txt b/requirements/static/ci/py3.11/windows.txt index 2b77f8cb01a..5116f080252 100644 --- a/requirements/static/ci/py3.11/windows.txt +++ b/requirements/static/ci/py3.11/windows.txt @@ -47,10 +47,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.11/windows.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests cffi==1.16.0 @@ -298,7 +298,7 @@ pyfakefs==5.3.1 # via -r requirements/pytest.txt pygit2==1.13.1 # via -r requirements/static/ci/windows.in -pymssql==2.2.7 ; sys_platform == "win32" +pymssql==2.3.1 ; sys_platform == "win32" # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # -r requirements/base.txt @@ -330,8 +330,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -400,11 +401,10 @@ pyzmq==25.1.2 # pytest-salt-factories requests-ntlm==1.2.0 # via pywinrm -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # docker # etcd3-py # kubernetes diff --git a/requirements/static/ci/py3.12/cloud.txt b/requirements/static/ci/py3.12/cloud.txt index 41f14034d18..4fae751b1d0 100644 --- a/requirements/static/ci/py3.12/cloud.txt +++ b/requirements/static/ci/py3.12/cloud.txt @@ -73,11 +73,11 @@ cassandra-driver==3.28.0 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests certvalidator==0.11.1 @@ -529,9 +529,10 @@ pytest-shell-utilities==1.8.0 # via # -c requirements/static/ci/py3.12/linux.txt # pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via # -c requirements/static/ci/py3.12/linux.txt + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -611,12 +612,11 @@ pyzmq==25.1.2 # pytest-salt-factories requests-ntlm==1.2.0 # via pywinrm -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.12/darwin.txt b/requirements/static/ci/py3.12/darwin.txt index f77e05e5ddf..4f5b37dfae2 100644 --- a/requirements/static/ci/py3.12/darwin.txt +++ b/requirements/static/ci/py3.12/darwin.txt @@ -55,10 +55,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/darwin.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests certvalidator==0.11.1 @@ -368,8 +368,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -430,11 +431,10 @@ pyzmq==25.1.2 # -c requirements/static/ci/../pkg/py3.12/darwin.txt # -r requirements/zeromq.txt # pytest-salt-factories -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/darwin.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.12/docs.txt b/requirements/static/ci/py3.12/docs.txt index acfaab6194d..259db0bdd15 100644 --- a/requirements/static/ci/py3.12/docs.txt +++ b/requirements/static/ci/py3.12/docs.txt @@ -28,9 +28,10 @@ autocommand==2.2.2 # jaraco.text babel==2.12.1 # via sphinx -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.12/linux.txt + # -r requirements/base.txt # requests cffi==1.16.0 # via @@ -218,7 +219,7 @@ pyzmq==25.1.2 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/base.txt diff --git a/requirements/static/ci/py3.12/freebsd.txt b/requirements/static/ci/py3.12/freebsd.txt index a293d6e0945..f0ddabf0883 100644 --- a/requirements/static/ci/py3.12/freebsd.txt +++ b/requirements/static/ci/py3.12/freebsd.txt @@ -55,10 +55,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/freebsd.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests certvalidator==0.11.1 @@ -372,8 +372,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -434,11 +435,10 @@ pyzmq==25.1.2 # -c requirements/static/ci/../pkg/py3.12/freebsd.txt # -r requirements/zeromq.txt # pytest-salt-factories -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/freebsd.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.12/lint.txt b/requirements/static/ci/py3.12/lint.txt index 2196c275bf3..5e9d7696612 100644 --- a/requirements/static/ci/py3.12/lint.txt +++ b/requirements/static/ci/py3.12/lint.txt @@ -88,11 +88,11 @@ cassandra-driver==3.28.0 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/static/ci/common.in -certifi==2023.7.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # httpcore # httpx # kubernetes @@ -604,12 +604,11 @@ redis==3.5.3 # via # -c requirements/static/ci/py3.12/linux.txt # redis-py-cluster -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.12/linux.txt b/requirements/static/ci/py3.12/linux.txt index 6e9d2f93e15..1b5c42d70d7 100644 --- a/requirements/static/ci/py3.12/linux.txt +++ b/requirements/static/ci/py3.12/linux.txt @@ -65,10 +65,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/linux.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # httpcore # httpx # kubernetes @@ -404,8 +404,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -476,11 +477,10 @@ redis-py-cluster==2.1.3 # via -r requirements/static/ci/linux.in redis==3.5.3 # via redis-py-cluster -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.12/windows.txt b/requirements/static/ci/py3.12/windows.txt index e4380533d61..0f40fd95a55 100644 --- a/requirements/static/ci/py3.12/windows.txt +++ b/requirements/static/ci/py3.12/windows.txt @@ -47,10 +47,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2024.7.4 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/windows.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests cffi==1.16.0 @@ -298,7 +298,7 @@ pyfakefs==5.3.1 # via -r requirements/pytest.txt pygit2==1.13.1 # via -r requirements/static/ci/windows.in -pymssql==2.2.7 ; sys_platform == "win32" +pymssql==2.3.1 ; sys_platform == "win32" # via # -c requirements/static/ci/../pkg/py3.12/windows.txt # -r requirements/base.txt @@ -330,8 +330,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -400,11 +401,10 @@ pyzmq==25.1.2 # pytest-salt-factories requests-ntlm==1.2.0 # via pywinrm -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via # -c requirements/static/ci/../pkg/py3.12/windows.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # docker # etcd3-py # kubernetes diff --git a/requirements/static/ci/py3.8/cloud.txt b/requirements/static/ci/py3.8/cloud.txt index 6dc77976817..ed7d16f8cd2 100644 --- a/requirements/static/ci/py3.8/cloud.txt +++ b/requirements/static/ci/py3.8/cloud.txt @@ -8,7 +8,7 @@ apache-libcloud==3.7.0 ; sys_platform != "win32" # via # -c requirements/static/ci/py3.8/linux.txt # -r requirements/static/ci/cloud.in -certifi==2023.07.22 +certifi==2023.07.22 ; python_version < "3.10" # via # -c requirements/static/ci/py3.8/linux.txt # requests @@ -51,7 +51,7 @@ pywinrm==0.4.3 # via -r requirements/static/ci/cloud.in requests-ntlm==1.2.0 # via pywinrm -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via # -c requirements/static/ci/py3.8/linux.txt # apache-libcloud diff --git a/requirements/static/ci/py3.8/docs.txt b/requirements/static/ci/py3.8/docs.txt index ff2a746c40a..8af149f9643 100644 --- a/requirements/static/ci/py3.8/docs.txt +++ b/requirements/static/ci/py3.8/docs.txt @@ -16,7 +16,7 @@ autocommand==2.2.2 # jaraco.text babel==2.12.1 # via sphinx -certifi==2023.07.22 +certifi==2023.07.22 ; python_version < "3.10" # via # -c requirements/static/ci/py3.8/linux.txt # requests @@ -128,7 +128,7 @@ pyyaml==6.0.1 # via # -c requirements/static/ci/py3.8/linux.txt # myst-docutils -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via # -c requirements/static/ci/py3.8/linux.txt # sphinx diff --git a/requirements/static/ci/py3.8/freebsd.txt b/requirements/static/ci/py3.8/freebsd.txt index 94c0f247d1a..2d8eb95d7f8 100644 --- a/requirements/static/ci/py3.8/freebsd.txt +++ b/requirements/static/ci/py3.8/freebsd.txt @@ -60,10 +60,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2023.07.22 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests certvalidator==0.11.1 @@ -383,8 +383,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -445,11 +446,10 @@ pyzmq==25.1.2 # -c requirements/static/ci/../pkg/py3.8/freebsd.txt # -r requirements/zeromq.txt # pytest-salt-factories -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.8/lint.txt b/requirements/static/ci/py3.8/lint.txt index 085b21ecd1d..30777ac3a8e 100644 --- a/requirements/static/ci/py3.8/lint.txt +++ b/requirements/static/ci/py3.8/lint.txt @@ -6,7 +6,7 @@ # astroid==3.1.0 # via pylint -certifi==2023.07.22 +certifi==2023.07.22 ; python_version < "3.10" # via # -c requirements/static/ci/py3.8/linux.txt # requests @@ -36,7 +36,7 @@ pylint==3.1.0 # via # -r requirements/static/ci/lint.in # saltpylint -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via # -c requirements/static/ci/py3.8/linux.txt # docker diff --git a/requirements/static/ci/py3.8/linux.txt b/requirements/static/ci/py3.8/linux.txt index 55faa78e308..c0000d4aefb 100644 --- a/requirements/static/ci/py3.8/linux.txt +++ b/requirements/static/ci/py3.8/linux.txt @@ -66,10 +66,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2023.07.22 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.8/linux.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # httpcore # httpx # kubernetes @@ -410,8 +410,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -481,11 +482,10 @@ redis-py-cluster==2.1.3 # via -r requirements/static/ci/linux.in redis==3.5.3 # via redis-py-cluster -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.8/linux.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.8/windows.txt b/requirements/static/ci/py3.8/windows.txt index 2099586ca2c..cc780bc39f8 100644 --- a/requirements/static/ci/py3.8/windows.txt +++ b/requirements/static/ci/py3.8/windows.txt @@ -52,10 +52,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2023.07.22 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.8/windows.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests cffi==1.16.0 @@ -309,7 +309,7 @@ pyfakefs==5.3.1 # via -r requirements/pytest.txt pygit2==1.13.1 # via -r requirements/static/ci/windows.in -pymssql==2.2.7 ; sys_platform == "win32" +pymssql==2.3.1 ; sys_platform == "win32" # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # -r requirements/base.txt @@ -341,8 +341,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -412,11 +413,10 @@ pyzmq==25.1.2 # pytest-salt-factories requests-ntlm==1.2.0 # via pywinrm -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # docker # etcd3-py # kubernetes diff --git a/requirements/static/ci/py3.9/cloud.txt b/requirements/static/ci/py3.9/cloud.txt index f3c308bb055..123f4758a6e 100644 --- a/requirements/static/ci/py3.9/cloud.txt +++ b/requirements/static/ci/py3.9/cloud.txt @@ -8,7 +8,7 @@ apache-libcloud==3.7.0 ; sys_platform != "win32" # via # -c requirements/static/ci/py3.9/linux.txt # -r requirements/static/ci/cloud.in -certifi==2023.07.22 +certifi==2023.07.22 ; python_version < "3.10" # via # -c requirements/static/ci/py3.9/linux.txt # requests @@ -51,7 +51,7 @@ pywinrm==0.4.3 # via -r requirements/static/ci/cloud.in requests-ntlm==1.2.0 # via pywinrm -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via # -c requirements/static/ci/py3.9/linux.txt # apache-libcloud diff --git a/requirements/static/ci/py3.9/darwin.txt b/requirements/static/ci/py3.9/darwin.txt index a364e46cc2a..5af323cd2be 100644 --- a/requirements/static/ci/py3.9/darwin.txt +++ b/requirements/static/ci/py3.9/darwin.txt @@ -60,10 +60,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2023.07.22 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests certvalidator==0.11.1 @@ -375,8 +375,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -437,11 +438,10 @@ pyzmq==25.1.2 # -c requirements/static/ci/../pkg/py3.9/darwin.txt # -r requirements/zeromq.txt # pytest-salt-factories -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.9/docs.txt b/requirements/static/ci/py3.9/docs.txt index 320123d7592..bc711d0b691 100644 --- a/requirements/static/ci/py3.9/docs.txt +++ b/requirements/static/ci/py3.9/docs.txt @@ -16,7 +16,7 @@ autocommand==2.2.2 # jaraco.text babel==2.12.1 # via sphinx -certifi==2023.07.22 +certifi==2023.07.22 ; python_version < "3.10" # via # -c requirements/static/ci/py3.9/linux.txt # requests @@ -123,7 +123,7 @@ pyyaml==6.0.1 # via # -c requirements/static/ci/py3.9/linux.txt # myst-docutils -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via # -c requirements/static/ci/py3.9/linux.txt # sphinx diff --git a/requirements/static/ci/py3.9/freebsd.txt b/requirements/static/ci/py3.9/freebsd.txt index d4a44882aea..6600b0c848f 100644 --- a/requirements/static/ci/py3.9/freebsd.txt +++ b/requirements/static/ci/py3.9/freebsd.txt @@ -60,10 +60,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2023.07.22 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests certvalidator==0.11.1 @@ -379,8 +379,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -441,11 +442,10 @@ pyzmq==25.1.2 # -c requirements/static/ci/../pkg/py3.9/freebsd.txt # -r requirements/zeromq.txt # pytest-salt-factories -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.9/lint.txt b/requirements/static/ci/py3.9/lint.txt index 92d500abf59..d41301640b9 100644 --- a/requirements/static/ci/py3.9/lint.txt +++ b/requirements/static/ci/py3.9/lint.txt @@ -6,7 +6,7 @@ # astroid==3.1.0 # via pylint -certifi==2023.07.22 +certifi==2023.07.22 ; python_version < "3.10" # via # -c requirements/static/ci/py3.9/linux.txt # requests @@ -36,7 +36,7 @@ pylint==3.1.0 # via # -r requirements/static/ci/lint.in # saltpylint -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via # -c requirements/static/ci/py3.9/linux.txt # docker diff --git a/requirements/static/ci/py3.9/linux.txt b/requirements/static/ci/py3.9/linux.txt index dd4d0c1630a..044d802af8a 100644 --- a/requirements/static/ci/py3.9/linux.txt +++ b/requirements/static/ci/py3.9/linux.txt @@ -66,10 +66,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2023.07.22 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.9/linux.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # httpcore # httpx # kubernetes @@ -406,8 +406,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -477,11 +478,10 @@ redis-py-cluster==2.1.3 # via -r requirements/static/ci/linux.in redis==3.5.3 # via redis-py-cluster -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.9/linux.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # apache-libcloud # docker # etcd3-py diff --git a/requirements/static/ci/py3.9/windows.txt b/requirements/static/ci/py3.9/windows.txt index 5c610222c6b..82ece83ae3a 100644 --- a/requirements/static/ci/py3.9/windows.txt +++ b/requirements/static/ci/py3.9/windows.txt @@ -52,10 +52,10 @@ cachetools==5.3.1 # via google-auth cassandra-driver==3.28.0 # via -r requirements/static/ci/common.in -certifi==2023.07.22 +certifi==2023.07.22 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.9/windows.txt - # -r requirements/static/ci/common.in + # -r requirements/base.txt # kubernetes # requests cffi==1.16.0 @@ -305,7 +305,7 @@ pyfakefs==5.3.1 # via -r requirements/pytest.txt pygit2==1.13.1 # via -r requirements/static/ci/windows.in -pymssql==2.2.7 ; sys_platform == "win32" +pymssql==2.3.1 ; sys_platform == "win32" # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # -r requirements/base.txt @@ -337,8 +337,9 @@ pytest-salt-factories==1.0.1 # via -r requirements/pytest.txt pytest-shell-utilities==1.8.0 # via pytest-salt-factories -pytest-skip-markers==1.5.0 +pytest-skip-markers==1.5.2 ; python_version >= "3.8" # via + # -r requirements/pytest.txt # pytest-salt-factories # pytest-shell-utilities # pytest-system-statistics @@ -408,11 +409,10 @@ pyzmq==25.1.2 # pytest-salt-factories requests-ntlm==1.2.0 # via pywinrm -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # -r requirements/base.txt - # -r requirements/static/ci/common.in # docker # etcd3-py # kubernetes diff --git a/requirements/static/pkg/py3.10/darwin.txt b/requirements/static/pkg/py3.10/darwin.txt index d5b06ea94c7..67bed9a326b 100644 --- a/requirements/static/pkg/py3.10/darwin.txt +++ b/requirements/static/pkg/py3.10/darwin.txt @@ -16,8 +16,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2024.7.4 ; python_version >= "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -113,7 +115,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.10/freebsd.txt b/requirements/static/pkg/py3.10/freebsd.txt index 3fd122e574d..a6286ee42f3 100644 --- a/requirements/static/pkg/py3.10/freebsd.txt +++ b/requirements/static/pkg/py3.10/freebsd.txt @@ -16,8 +16,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2024.7.4 ; python_version >= "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -113,7 +115,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.10/linux.txt b/requirements/static/pkg/py3.10/linux.txt index 7f7f059baf3..832feb52085 100644 --- a/requirements/static/pkg/py3.10/linux.txt +++ b/requirements/static/pkg/py3.10/linux.txt @@ -16,8 +16,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2024.7.4 ; python_version >= "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -113,7 +115,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via -r requirements/base.txt rpm-vercmp==0.1.2 ; sys_platform == "linux" # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.10/windows.txt b/requirements/static/pkg/py3.10/windows.txt index b44be8971f8..7b131da556f 100644 --- a/requirements/static/pkg/py3.10/windows.txt +++ b/requirements/static/pkg/py3.10/windows.txt @@ -16,8 +16,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2024.7.4 ; python_version >= "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via # clr-loader @@ -105,7 +107,7 @@ pydantic-core==2.16.3 # via pydantic pydantic==2.6.4 # via inflect -pymssql==2.2.7 ; sys_platform == "win32" +pymssql==2.3.1 ; sys_platform == "win32" # via -r requirements/base.txt pymysql==1.1.0 ; sys_platform == "win32" # via -r requirements/base.txt @@ -127,7 +129,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.11/darwin.txt b/requirements/static/pkg/py3.11/darwin.txt index db6c4d08a5a..7d16335c179 100644 --- a/requirements/static/pkg/py3.11/darwin.txt +++ b/requirements/static/pkg/py3.11/darwin.txt @@ -14,8 +14,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2024.7.4 ; python_version >= "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -111,7 +113,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.11/freebsd.txt b/requirements/static/pkg/py3.11/freebsd.txt index 99d25ff7113..b42b9373574 100644 --- a/requirements/static/pkg/py3.11/freebsd.txt +++ b/requirements/static/pkg/py3.11/freebsd.txt @@ -14,8 +14,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2024.7.4 ; python_version >= "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -111,7 +113,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.11/linux.txt b/requirements/static/pkg/py3.11/linux.txt index 4f25b2b1030..e5c4d095957 100644 --- a/requirements/static/pkg/py3.11/linux.txt +++ b/requirements/static/pkg/py3.11/linux.txt @@ -14,8 +14,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2024.7.4 ; python_version >= "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -111,7 +113,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via -r requirements/base.txt rpm-vercmp==0.1.2 ; sys_platform == "linux" # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.11/windows.txt b/requirements/static/pkg/py3.11/windows.txt index 6815187d2a4..8c3468258e4 100644 --- a/requirements/static/pkg/py3.11/windows.txt +++ b/requirements/static/pkg/py3.11/windows.txt @@ -14,8 +14,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2024.7.4 ; python_version >= "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via # clr-loader @@ -103,7 +105,7 @@ pydantic-core==2.16.3 # via pydantic pydantic==2.6.4 # via inflect -pymssql==2.2.7 ; sys_platform == "win32" +pymssql==2.3.1 ; sys_platform == "win32" # via -r requirements/base.txt pymysql==1.1.0 ; sys_platform == "win32" # via -r requirements/base.txt @@ -125,7 +127,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.12/darwin.txt b/requirements/static/pkg/py3.12/darwin.txt index 51020f0170a..2d91e153bbf 100644 --- a/requirements/static/pkg/py3.12/darwin.txt +++ b/requirements/static/pkg/py3.12/darwin.txt @@ -14,8 +14,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2024.7.4 ; python_version >= "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -111,7 +113,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.12/freebsd.txt b/requirements/static/pkg/py3.12/freebsd.txt index 95bedeebbe5..b8fc5f59742 100644 --- a/requirements/static/pkg/py3.12/freebsd.txt +++ b/requirements/static/pkg/py3.12/freebsd.txt @@ -14,8 +14,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2024.7.4 ; python_version >= "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -111,7 +113,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.12/linux.txt b/requirements/static/pkg/py3.12/linux.txt index 1da1d6f25a0..f048c510ee8 100644 --- a/requirements/static/pkg/py3.12/linux.txt +++ b/requirements/static/pkg/py3.12/linux.txt @@ -14,8 +14,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2024.7.4 ; python_version >= "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -111,7 +113,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via -r requirements/base.txt rpm-vercmp==0.1.2 ; sys_platform == "linux" # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.12/windows.txt b/requirements/static/pkg/py3.12/windows.txt index 3b5b7009c04..01f9e4c4669 100644 --- a/requirements/static/pkg/py3.12/windows.txt +++ b/requirements/static/pkg/py3.12/windows.txt @@ -14,8 +14,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2024.7.4 ; python_version >= "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via # clr-loader @@ -103,7 +105,7 @@ pydantic-core==2.16.3 # via pydantic pydantic==2.6.4 # via inflect -pymssql==2.2.7 ; sys_platform == "win32" +pymssql==2.3.1 ; sys_platform == "win32" # via -r requirements/base.txt pymysql==1.1.0 ; sys_platform == "win32" # via -r requirements/base.txt @@ -125,7 +127,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.32.3 ; python_version >= "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.8/freebsd.txt b/requirements/static/pkg/py3.8/freebsd.txt index 1097495bedd..2becd72d10f 100644 --- a/requirements/static/pkg/py3.8/freebsd.txt +++ b/requirements/static/pkg/py3.8/freebsd.txt @@ -16,8 +16,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2023.07.22 ; python_version < "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -115,7 +117,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.8/linux.txt b/requirements/static/pkg/py3.8/linux.txt index e144ef6b5a0..843b5c5e189 100644 --- a/requirements/static/pkg/py3.8/linux.txt +++ b/requirements/static/pkg/py3.8/linux.txt @@ -16,8 +16,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2023.07.22 ; python_version < "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -115,7 +117,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via -r requirements/base.txt rpm-vercmp==0.1.2 ; sys_platform == "linux" # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.8/windows.txt b/requirements/static/pkg/py3.8/windows.txt index 11d7cf6d112..fd7e78c8c22 100644 --- a/requirements/static/pkg/py3.8/windows.txt +++ b/requirements/static/pkg/py3.8/windows.txt @@ -16,8 +16,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2023.07.22 ; python_version < "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via # clr-loader @@ -107,7 +109,7 @@ pydantic-core==2.16.3 # via pydantic pydantic==2.6.4 # via inflect -pymssql==2.2.7 ; sys_platform == "win32" +pymssql==2.3.1 ; sys_platform == "win32" # via -r requirements/base.txt pymysql==1.1.0 ; sys_platform == "win32" # via -r requirements/base.txt @@ -130,7 +132,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.9/darwin.txt b/requirements/static/pkg/py3.9/darwin.txt index 47ef9bfda36..7635b7f0fa4 100644 --- a/requirements/static/pkg/py3.9/darwin.txt +++ b/requirements/static/pkg/py3.9/darwin.txt @@ -16,8 +16,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2023.07.22 ; python_version < "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -113,7 +115,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.9/freebsd.txt b/requirements/static/pkg/py3.9/freebsd.txt index bde4cdef69b..698679ed73a 100644 --- a/requirements/static/pkg/py3.9/freebsd.txt +++ b/requirements/static/pkg/py3.9/freebsd.txt @@ -16,8 +16,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2023.07.22 ; python_version < "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -113,7 +115,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.9/linux.txt b/requirements/static/pkg/py3.9/linux.txt index 44e4bb6ef18..99b1e302ec2 100644 --- a/requirements/static/pkg/py3.9/linux.txt +++ b/requirements/static/pkg/py3.9/linux.txt @@ -16,8 +16,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2023.07.22 ; python_version < "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via cryptography charset-normalizer==3.2.0 @@ -113,7 +115,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via -r requirements/base.txt rpm-vercmp==0.1.2 ; sys_platform == "linux" # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.9/windows.txt b/requirements/static/pkg/py3.9/windows.txt index b85fe898451..1ae14818952 100644 --- a/requirements/static/pkg/py3.9/windows.txt +++ b/requirements/static/pkg/py3.9/windows.txt @@ -16,8 +16,10 @@ attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text -certifi==2023.07.22 - # via requests +certifi==2023.07.22 ; python_version < "3.10" + # via + # -r requirements/base.txt + # requests cffi==1.16.0 # via # clr-loader @@ -105,7 +107,7 @@ pydantic-core==2.16.3 # via pydantic pydantic==2.6.4 # via inflect -pymssql==2.2.7 ; sys_platform == "win32" +pymssql==2.3.1 ; sys_platform == "win32" # via -r requirements/base.txt pymysql==1.1.0 ; sys_platform == "win32" # via -r requirements/base.txt @@ -128,7 +130,7 @@ pyyaml==6.0.1 # via -r requirements/base.txt pyzmq==25.1.2 # via -r requirements/zeromq.txt -requests==2.32.3 ; python_version >= "3.8" +requests==2.31.0 ; python_version < "3.10" # via -r requirements/base.txt setproctitle==1.3.2 # via -r requirements/base.txt diff --git a/salt/channel/client.py b/salt/channel/client.py index 3483e7237e4..b03f4659a17 100644 --- a/salt/channel/client.py +++ b/salt/channel/client.py @@ -368,7 +368,7 @@ class AsyncPubChannel: async_methods = [ "connect", - "_decode_messages", + "_decode_payload", ] close_methods = [ "close", diff --git a/salt/cli/batch.py b/salt/cli/batch.py index 2e43b0ee22b..3a648c02b86 100644 --- a/salt/cli/batch.py +++ b/salt/cli/batch.py @@ -83,7 +83,14 @@ class Batch: ) break if m is not None: - fret.add(m) + if "failed" in ret[m] and ret[m]["failed"] is True: + log.debug( + "minion '%s' failed test.ping - will be returned as a down minion", + m, + ) + else: + fret.add(m) + return (list(fret), ping_gen, nret.difference(fret)) def get_bnum(self): @@ -292,11 +299,12 @@ class Batch: # We already know some minions didn't respond to the ping, so inform # inform user attempt to run a job failed salt.utils.stringutils.print_cli( - "Minion '%s' failed to respond to job sent", minion + f"Minion '{minion}' failed to respond to job sent" ) if self.opts.get("failhard"): failhard = True + ret[minion] = data else: # If we are executing multiple modules with the same cmd, # We use the highest retcode. diff --git a/salt/grains/core.py b/salt/grains/core.py index 80a4d181adc..af159702fe6 100644 --- a/salt/grains/core.py +++ b/salt/grains/core.py @@ -1868,6 +1868,7 @@ _OS_FAMILY_MAP = { "SLES_SAP": "Suse", "Arch ARM": "Arch", "Manjaro": "Arch", + "Manjaro ARM": "Arch", "Antergos": "Arch", "EndeavourOS": "Arch", "ALT": "RedHat", diff --git a/salt/modules/cmdmod.py b/salt/modules/cmdmod.py index c92a4aa4195..1891c044fb0 100644 --- a/salt/modules/cmdmod.py +++ b/salt/modules/cmdmod.py @@ -290,7 +290,17 @@ def _prep_powershell_cmd(win_shell, cmd, encoded_cmd): # Strip whitespace if isinstance(cmd, list): cmd = " ".join(cmd) - new_cmd.extend(["-Command", f"& {{{cmd.strip()}}}"]) + + # Commands that are a specific keyword behave differently. They fail if + # you add a "&" to the front. Add those here as we find them: + keywords = ["$", "&", ".", "Configuration"] + + for keyword in keywords: + if cmd.startswith(keyword): + new_cmd.extend(["-Command", f"{cmd.strip()}"]) + break + else: + new_cmd.extend(["-Command", f"& {cmd.strip()}"]) log.debug(new_cmd) return new_cmd @@ -2689,11 +2699,15 @@ def script( :param str args: String of command line args to pass to the script. Only used if no args are specified as part of the `name` argument. To pass a - string containing spaces in YAML, you will need to doubly-quote it: + string containing spaces in YAML, you will need to doubly-quote it. + Additionally, if you need to pass falsey values (e.g., "0", "", "False"), + you should doubly-quote them to ensure they are correctly interpreted: .. code-block:: bash salt myminion cmd.script salt://foo.sh "arg1 'arg two' arg3" + salt myminion cmd.script salt://foo.sh "''0''" + salt myminion cmd.script salt://foo.sh "''False''" :param str cwd: The directory from which to execute the command. Defaults to the directory returned from Python's tempfile.mkstemp. @@ -2835,6 +2849,10 @@ def script( .. versionadded:: 2019.2.0 + :return: The return value of the script execution, including stdout, stderr, + and the exit code. If the script returns a falsey string value, it should be + doubly-quoted to ensure it is correctly interpreted by Salt. + CLI Example: .. code-block:: bash @@ -4096,6 +4114,7 @@ def powershell( cmd = salt.utils.stringutils.to_str(cmd) encoded_cmd = True else: + cmd = f"{{{cmd}}}" encoded_cmd = False # Retrieve the response, while overriding shell with 'powershell' diff --git a/salt/modules/file.py b/salt/modules/file.py index 3290ddc6c21..19b2bf10de0 100644 --- a/salt/modules/file.py +++ b/salt/modules/file.py @@ -2641,7 +2641,7 @@ def replace( r_data = mmap.mmap(r_file.fileno(), 0, access=mmap.ACCESS_READ) except (ValueError, OSError): # size of file in /proc is 0, but contains data - r_data = salt.utils.stringutils.to_bytes("".join(r_file)) + r_data = b"".join(r_file) if search_only: # Just search; bail as early as a match is found if re.search(cpattern, r_data): diff --git a/salt/modules/ini_manage.py b/salt/modules/ini_manage.py index f5aff8881e2..6935cf2c0be 100644 --- a/salt/modules/ini_manage.py +++ b/salt/modules/ini_manage.py @@ -37,7 +37,7 @@ COM_REGX = re.compile(r"^\s*(#|;)\s*(.*)") INDENTED_REGX = re.compile(r"(\s+)(.*)") -def set_option(file_name, sections=None, separator="=", encoding=None): +def set_option(file_name, sections=None, separator="=", encoding=None, no_spaces=False): """ Edit an ini file, replacing one or more sections. Returns a dictionary containing the changes made. @@ -66,6 +66,18 @@ def set_option(file_name, sections=None, separator="=", encoding=None): .. versionadded:: 3006.6 + no_spaces (bool): + A bool value that specifies that the key/value separator will be + wrapped with spaces. This parameter was added to have the ability to + not wrap the separator with spaces. Default is ``False``, which + maintains backwards compatibility. + + .. warning:: + This will affect all key/value pairs in the ini file, not just + the specific value being set. + + .. versionadded:: 3006.10 + Returns: dict: A dictionary representing the changes made to the ini file @@ -88,7 +100,9 @@ def set_option(file_name, sections=None, separator="=", encoding=None): """ sections = sections or {} - inifile = _Ini.get_ini_file(file_name, separator=separator, encoding=encoding) + inifile = _Ini.get_ini_file( + file_name, separator=separator, encoding=encoding, no_spaces=no_spaces + ) changes = inifile.update(sections) inifile.flush() return changes @@ -388,18 +402,19 @@ def get_ini(file_name, separator="=", encoding=None): class _Section(OrderedDict): - def __init__(self, name, inicontents="", separator="=", commenter="#"): + def __init__( + self, name, inicontents="", separator="=", commenter="#", no_spaces=False + ): super().__init__(self) self.name = name self.inicontents = inicontents self.sep = separator self.com = commenter + self.no_spaces = no_spaces opt_regx_prefix = r"(\s*)(.+?)\s*" opt_regx_suffix = r"\s*(.*)\s*" - self.opt_regx_str = r"{}(\{}){}".format( - opt_regx_prefix, self.sep, opt_regx_suffix - ) + self.opt_regx_str = rf"{opt_regx_prefix}(\{self.sep}){opt_regx_suffix}" self.opt_regx = re.compile(self.opt_regx_str) def refresh(self, inicontents=None): @@ -475,7 +490,11 @@ class _Section(OrderedDict): # Ensure the value is either a _Section or a string if isinstance(value, (dict, OrderedDict)): sect = _Section( - name=key, inicontents="", separator=self.sep, commenter=self.com + name=key, + inicontents="", + separator=self.sep, + commenter=self.com, + no_spaces=self.no_spaces, ) sect.update(value) value = sect @@ -507,7 +526,7 @@ class _Section(OrderedDict): return changes def gen_ini(self): - yield "{0}[{1}]{0}".format(os.linesep, self.name) + yield f"{os.linesep}[{self.name}]{os.linesep}" sections_dict = OrderedDict() for name, value in self.items(): # Handle Comment Lines @@ -517,14 +536,20 @@ class _Section(OrderedDict): elif isinstance(value, _Section): sections_dict.update({name: value}) # Key / Value pairs - # Adds spaces between the separator else: - yield "{}{}{}{}".format( - name, - f" {self.sep} " if self.sep != " " else self.sep, - value, - os.linesep, - ) + # multiple spaces will be a single space + if all(c == " " for c in self.sep): + self.sep = " " + # Default is to add spaces + if self.no_spaces: + if self.sep != " ": + # We only strip whitespace if the delimiter is not a space + self.sep = self.sep.strip() + else: + if self.sep != " ": + # We only add spaces if the delimiter itself is not a space + self.sep = f" {self.sep.strip()} " + yield f"{name}{self.sep}{value}{os.linesep}" for name, value in sections_dict.items(): yield from value.gen_ini() @@ -557,15 +582,26 @@ class _Section(OrderedDict): class _Ini(_Section): def __init__( - self, name, inicontents="", separator="=", commenter="#", encoding=None + self, + name, + inicontents="", + separator="=", + commenter="#", + encoding=None, + no_spaces=False, ): super().__init__( - self, inicontents=inicontents, separator=separator, commenter=commenter + self, + inicontents=inicontents, + separator=separator, + commenter=commenter, + no_spaces=no_spaces, ) self.name = name if encoding is None: encoding = __salt_system_encoding__ self.encoding = encoding + self.no_spaces = no_spaces def refresh(self, inicontents=None): if inicontents is None: @@ -612,7 +648,7 @@ class _Ini(_Section): self.name, "w", encoding=self.encoding ) as outfile: ini_gen = self.gen_ini() - next(ini_gen) + next(ini_gen) # Next to skip the file name ini_gen_list = list(ini_gen) # Avoid writing an initial line separator. if ini_gen_list: @@ -624,8 +660,10 @@ class _Ini(_Section): ) @staticmethod - def get_ini_file(file_name, separator="=", encoding=None): - inifile = _Ini(file_name, separator=separator, encoding=encoding) + def get_ini_file(file_name, separator="=", encoding=None, no_spaces=False): + inifile = _Ini( + file_name, separator=separator, encoding=encoding, no_spaces=no_spaces + ) inifile.refresh() return inifile diff --git a/salt/modules/virtualenv_mod.py b/salt/modules/virtualenv_mod.py index 042847fde40..cd52435e6f5 100644 --- a/salt/modules/virtualenv_mod.py +++ b/salt/modules/virtualenv_mod.py @@ -27,7 +27,7 @@ KNOWN_BINARY_NAMES = frozenset( log = logging.getLogger(__name__) -__opts__ = {"venv_bin": salt.utils.path.which_bin(KNOWN_BINARY_NAMES) or "virtualenv"} +__opts__ = {"venv_bin": salt.utils.path.which_bin(KNOWN_BINARY_NAMES) or "venv"} __pillar__ = {} @@ -101,7 +101,7 @@ def create( Defaults to ``virtualenv``. system_site_packages : False - Passthrough argument given to virtualenv or pyvenv + Passthrough argument given to virtualenv or venv distribute : False Passthrough argument given to virtualenv @@ -111,7 +111,7 @@ def create( ``distribute=True`` clear : False - Passthrough argument given to virtualenv or pyvenv + Passthrough argument given to virtualenv or venv python : None (default) Passthrough argument given to virtualenv @@ -126,10 +126,10 @@ def create( Passthrough argument given to virtualenv if not None symlinks : None - Passthrough argument given to pyvenv if True + Passthrough argument given to venv if True upgrade : None - Passthrough argument given to pyvenv if True + Passthrough argument given to venv if True user : None Set ownership for the virtualenv @@ -174,12 +174,15 @@ def create( - VIRTUALENV_ALWAYS_COPY: 1 """ if venv_bin is None: - venv_bin = __opts__.get("venv_bin") or __pillar__.get("venv_bin") + venv_bin = __pillar__.get("venv_bin") or __opts__.get("venv_bin") - cmd = [venv_bin] + if venv_bin == "venv": + cmd = [sys.executable, "-m", "venv"] + else: + cmd = [venv_bin] - if "pyvenv" not in venv_bin: - # ----- Stop the user if pyvenv only options are used ---------------> + if "venv" not in venv_bin: + # ----- Stop the user if venv only options are used -----------------> # If any of the following values are not None, it means that the user # is actually passing a True or False value. Stop Him! if upgrade is not None: @@ -194,7 +197,7 @@ def create( venv_bin ) ) - # <---- Stop the user if pyvenv only options are used ---------------- + # <---- Stop the user if venv only options are used ------------------ virtualenv_version_info = virtualenv_ver(venv_bin, user=user, **kwargs) @@ -264,7 +267,7 @@ def create( if symlinks is True: cmd.append("--symlinks") - # Common options to virtualenv and pyvenv + # Common options to virtualenv and venv if clear is True: cmd.append("--clear") if system_site_packages is True: diff --git a/salt/modules/win_status.py b/salt/modules/win_status.py index 2becc4cb19b..41b59bf11f6 100644 --- a/salt/modules/win_status.py +++ b/salt/modules/win_status.py @@ -497,8 +497,16 @@ def _get_connected_ips(port): for conn in conns: if conn.status == psutil.CONN_ESTABLISHED: - if conn.laddr.port == port: - connected_ips.add(conn.laddr.ip) + if conn.raddr.port == port: + log.debug( + "%s %s:%s --> %s:%s", + conn.status, + conn.laddr.ip, + conn.laddr.port, + conn.raddr.ip, + conn.raddr.port, + ) + connected_ips.add(conn.raddr.ip) return connected_ips diff --git a/salt/output/profile.py b/salt/output/profile.py index 8f54c7017f4..87a93c17bba 100644 --- a/salt/output/profile.py +++ b/salt/output/profile.py @@ -44,12 +44,19 @@ def _find_durations(data, name_max=60): ml = len("duration (ms)") for host in data: for sid in data[host]: - dat = data[host][sid] - ts = sid.split("_|-") + try: + dat = data[host][sid] + except TypeError: + dat = {} + + if not isinstance(dat, dict): + dat = {"name": str(sid)} + + ts = str(sid).split("_|-") mod = ts[0] fun = ts[-1] name = dat.get("name", dat.get("__id__")) - dur = float(data[host][sid].get("duration", -1)) + dur = float(dat.get("duration", -1)) if name is None: name = "<>" diff --git a/salt/pillar/__init__.py b/salt/pillar/__init__.py index cb910307871..12042762ca7 100644 --- a/salt/pillar/__init__.py +++ b/salt/pillar/__init__.py @@ -197,6 +197,15 @@ class RemotePillarMixin: log.trace("ext_pillar_extra_data = %s", extra_data) return extra_data + def validate_return(self, data): + if not isinstance(data, dict): + msg = "Got a bad pillar from master, type {}, expecting dict: {}".format( + type(data).__name__, data + ) + log.error(msg) + # raise an exception! Pillar isn't empty, we can't sync it! + raise SaltClientError(msg) + class AsyncRemotePillar(RemotePillarMixin): """ @@ -276,14 +285,7 @@ class AsyncRemotePillar(RemotePillarMixin): except Exception: # pylint: disable=broad-except log.exception("Exception getting pillar:") raise SaltClientError("Exception getting pillar.") - - if not isinstance(ret_pillar, dict): - msg = "Got a bad pillar from master, type {}, expecting dict: {}".format( - type(ret_pillar).__name__, ret_pillar - ) - log.error(msg) - # raise an exception! Pillar isn't empty, we can't sync it! - raise SaltClientError(msg) + self.validate_return(ret_pillar) raise tornado.gen.Return(ret_pillar) def destroy(self): @@ -374,14 +376,7 @@ class RemotePillar(RemotePillarMixin): except Exception: # pylint: disable=broad-except log.exception("Exception getting pillar:") raise SaltClientError("Exception getting pillar.") - - if not isinstance(ret_pillar, dict): - log.error( - "Got a bad pillar from master, type %s, expecting dict: %s", - type(ret_pillar).__name__, - ret_pillar, - ) - return {} + self.validate_return(ret_pillar) return ret_pillar def destroy(self): diff --git a/salt/scripts.py b/salt/scripts.py index 662104a7142..4e0faff3004 100644 --- a/salt/scripts.py +++ b/salt/scripts.py @@ -162,9 +162,12 @@ def salt_minion(): """ import signal + import salt.utils.debug import salt.utils.platform import salt.utils.process + salt.utils.debug.enable_sigusr1_handler() + salt.utils.process.notify_systemd() import multiprocessing diff --git a/salt/state.py b/salt/state.py index 2f9d44f9f41..66cc8ec88c6 100644 --- a/salt/state.py +++ b/salt/state.py @@ -39,6 +39,7 @@ import salt.utils.event import salt.utils.files import salt.utils.hashutils import salt.utils.immutabletypes as immutabletypes +import salt.utils.jid import salt.utils.msgpack import salt.utils.platform import salt.utils.process @@ -761,7 +762,21 @@ class State: loader="states", initial_pillar=None, file_client=None, + __invocation_id=None, ): + """ + When instantiating an object of this class, do not pass + ``__invocation_id``. It is an internal field for tracking + parallel executions where no jid is available (Salt-SSH) and + only exposed as an init argument to work on spawning platforms. + """ + if jid is not None: + __invocation_id = jid + if __invocation_id is None: + # For salt-ssh parallel states, we need a unique identifier + # for a single execution. self.jid should not be set there + # since it's used for other purposes as well. + __invocation_id = salt.utils.jid.gen_jid(opts) self._init_kwargs = { "opts": opts, "pillar_override": pillar_override, @@ -772,6 +787,7 @@ class State: "mocked": mocked, "loader": loader, "initial_pillar": initial_pillar, + "__invocation_id": __invocation_id, } self.states_loader = loader if "grains" not in opts: @@ -818,6 +834,7 @@ class State: self.pre = {} self.__run_num = 0 self.jid = jid + self.invocation_id = __invocation_id self.instance_id = str(id(self)) self.inject_globals = {} self.mocked = mocked @@ -2243,7 +2260,7 @@ class State: ] ) - troot = os.path.join(instance.opts["cachedir"], instance.jid) + troot = os.path.join(instance.opts["cachedir"], instance.invocation_id) tfile = os.path.join(troot, salt.utils.hashutils.sha1_digest(tag)) if not os.path.isdir(troot): try: @@ -2827,7 +2844,7 @@ class State: if not proc.is_alive(): ret_cache = os.path.join( self.opts["cachedir"], - self.jid, + self.invocation_id, salt.utils.hashutils.sha1_digest(tag), ) if not os.path.isfile(ret_cache): diff --git a/salt/states/ini_manage.py b/salt/states/ini_manage.py index 9851d792734..eec3ab843f6 100644 --- a/salt/states/ini_manage.py +++ b/salt/states/ini_manage.py @@ -21,8 +21,61 @@ def __virtual__(): return __virtualname__ if "ini.set_option" in __salt__ else False -def options_present(name, sections=None, separator="=", strict=False): +def options_present( + name, sections=None, separator="=", strict=False, encoding=None, no_spaces=False +): """ + Set or create a key/value pair in an ``ini`` file. Options present in the + ini file and not specified in the sections dict will be untouched, unless + the ``strict: True`` flag is used. + + Sections that do not exist will be created. + + Args: + + name (str): + The path to the ini file + + sections (dict): + A dictionary of sections and key/value pairs that will be used to + update the ini file. Other sections and key/value pairs in the ini + file will be untouched unless ``strict: True`` is passed. + + separator (str): + The character used to separate keys and values. Standard ini files + use the "=" character. The default is ``=``. + + strict (bool): + A boolean value that specifies that the ``sections`` dictionary + contains all settings in the ini file. ``True`` will create an ini + file with only the values specified in ``sections``. ``False`` will + append or update values in an existing ini file and leave the rest + untouched. + + encoding (str): + A string value representing encoding of the target ini file. If + ``None`` is passed, it uses the system default which is likely + ``utf-8``. Default is ``None`` + + .. versionadded:: 3006.10 + + no_spaces (bool): + A bool value that specifies that the key/value separator will be + wrapped with spaces. This parameter was added to have the ability to + not wrap the separator with spaces. Default is ``False``, which + maintains backwards compatibility. + + .. warning:: + This will affect all key/value pairs in the ini file, not just + the specific value being set. + + .. versionadded:: 3006.10 + + Returns: + dict: A dictionary containing list of changes made + + Example: + .. code-block:: yaml /home/saltminion/api-paste.ini: @@ -35,12 +88,6 @@ def options_present(name, sections=None, separator="=", strict=False): secondoption: 'secondvalue' test1: testkey1: 'testval121' - - options present in file and not specified in sections - dict will be untouched, unless `strict: True` flag is - used - - changes dict will contain the list of changes made """ ret = { "name": name, @@ -58,7 +105,9 @@ def options_present(name, sections=None, separator="=", strict=False): for sname, sbody in sections.items(): if not isinstance(sbody, (dict, OrderedDict)): options.update({sname: sbody}) - cur_ini = __salt__["ini.get_ini"](name, separator) + cur_ini = __salt__["ini.get_ini"]( + file_name=name, separator=separator, encoding=encoding + ) original_top_level_opts = {} original_sections = {} for key, val in cur_ini.items(): @@ -78,7 +127,13 @@ def options_present(name, sections=None, separator="=", strict=False): ret["comment"] += f"Changed key {option}.\n" ret["result"] = None else: - options_updated = __salt__["ini.set_option"](name, options, separator) + options_updated = __salt__["ini.set_option"]( + file_name=name, + sections=options, + separator=separator, + encoding=encoding, + no_spaces=no_spaces, + ) changes.update(options_updated) if strict: for opt_to_remove in set(original_top_level_opts).difference(options): @@ -87,7 +142,11 @@ def options_present(name, sections=None, separator="=", strict=False): ret["result"] = None else: __salt__["ini.remove_option"]( - name, None, opt_to_remove, separator + file_name=name, + section=None, + option=opt_to_remove, + separator=separator, + encoding=encoding, ) changes.update( { @@ -119,7 +178,11 @@ def options_present(name, sections=None, separator="=", strict=False): ret["result"] = None else: __salt__["ini.remove_option"]( - name, section_name, key_to_remove, separator + file_name=name, + section=section_name, + option=key_to_remove, + separator=separator, + encoding=encoding, ) changes[section_name].update({key_to_remove: ""}) changes[section_name].update( @@ -140,7 +203,11 @@ def options_present(name, sections=None, separator="=", strict=False): ret["result"] = None else: options_updated = __salt__["ini.set_option"]( - name, {section_name: section_body}, separator + file_name=name, + sections={section_name: section_body}, + separator=separator, + encoding=encoding, + no_spaces=no_spaces, ) if options_updated: changes[section_name].update(options_updated[section_name]) @@ -148,7 +215,13 @@ def options_present(name, sections=None, separator="=", strict=False): del changes[section_name] else: if not __opts__["test"]: - changes = __salt__["ini.set_option"](name, sections, separator) + changes = __salt__["ini.set_option"]( + file_name=name, + sections=sections, + separator=separator, + encoding=encoding, + no_spaces=no_spaces, + ) except (OSError, KeyError) as err: ret["comment"] = f"{err}" ret["result"] = False @@ -165,8 +238,37 @@ def options_present(name, sections=None, separator="=", strict=False): return ret -def options_absent(name, sections=None, separator="="): +def options_absent(name, sections=None, separator="=", encoding=None): """ + Remove a key/value pair from an ini file. Key/value pairs present in the ini + file and not specified in sections dict will be untouched. + + Args: + + name (str): + The path to the ini file + + sections (dict): + A dictionary of sections and key/value pairs that will be removed + from the ini file. Other key/value pairs in the ini file will be + untouched. + + separator (str): + The character used to separate keys and values. Standard ini files + use the "=" character. The default is ``=``. + + encoding (str): + A string value representing encoding of the target ini file. If + ``None`` is passed, it uses the system default which is likely + ``utf-8``. Default is ``None`` + + .. versionadded:: 3006.10 + + Returns: + dict: A dictionary containing list of changes made + + Example: + .. code-block:: yaml /home/saltminion/api-paste.ini: @@ -178,11 +280,6 @@ def options_absent(name, sections=None, separator="="): - secondoption test1: - testkey1 - - options present in file and not specified in sections - dict will be untouched - - changes dict will contain the list of changes made """ ret = { "name": name, @@ -196,7 +293,12 @@ def options_absent(name, sections=None, separator="="): for section in sections or {}: section_name = " in section " + section if section else "" try: - cur_section = __salt__["ini.get_section"](name, section, separator) + cur_section = __salt__["ini.get_section"]( + file_name=name, + section=section, + separator=separator, + encoding=encoding, + ) except OSError as err: ret["comment"] = f"{err}" ret["result"] = False @@ -215,7 +317,13 @@ def options_absent(name, sections=None, separator="="): ret["result"] = None else: option = section - if not __salt__["ini.get_option"](name, None, option, separator): + if not __salt__["ini.get_option"]( + file_name=name, + section=None, + option=option, + separator=separator, + encoding=encoding, + ): ret["comment"] += f"Key {option} does not exist.\n" continue ret["comment"] += f"Deleted key {option}.\n" @@ -229,7 +337,11 @@ def options_absent(name, sections=None, separator="="): for key in keys: try: current_value = __salt__["ini.remove_option"]( - name, section, key, separator + file_name=name, + section=section, + option=key, + separator=separator, + encoding=encoding, ) except OSError as err: ret["comment"] = f"{err}" @@ -247,8 +359,38 @@ def options_absent(name, sections=None, separator="="): return ret -def sections_present(name, sections=None, separator="="): +def sections_present(name, sections=None, separator="=", encoding=None): """ + Add sections to an ini file. This will only create empty sections. To also + create key/value pairs, use options_present state. + + Args: + + name (str): + The path to the ini file + + sections (dict): + A dictionary of sections and key/value pairs that will be used to + update the ini file. Only the sections portion is used, key/value + pairs are ignored. To also set key/value pairs, use the + options_present state. + + separator (str): + The character used to separate keys and values. Standard ini files + use the "=" character. The default is ``=``. + + encoding (str): + A string value representing encoding of the target ini file. If + ``None`` is passed, it uses the system default which is likely + ``utf-8``. Default is ``None`` + + .. versionadded:: 3006.10 + + Returns: + dict: A dictionary containing list of changes made + + Example: + .. code-block:: yaml /home/saltminion/api-paste.ini: @@ -257,12 +399,6 @@ def sections_present(name, sections=None, separator="="): - sections: - section_one - section_two - - This will only create empty sections. To also create options, use - options_present state - - options present in file and not specified in sections will be deleted - changes dict will contain the sections that changed """ ret = { "name": name, @@ -274,7 +410,9 @@ def sections_present(name, sections=None, separator="="): ret["result"] = True ret["comment"] = "" try: - cur_ini = __salt__["ini.get_ini"](name, separator) + cur_ini = __salt__["ini.get_ini"]( + file_name=name, separator=separator, encoding=encoding + ) except OSError as err: ret["result"] = False ret["comment"] = f"{err}" @@ -293,7 +431,12 @@ def sections_present(name, sections=None, separator="="): for section_name in sections or []: section_to_update.update({section_name: {}}) try: - changes = __salt__["ini.set_option"](name, section_to_update, separator) + changes = __salt__["ini.set_option"]( + file_name=name, + section=section_to_update, + separator=separator, + encoding=encoding, + ) except OSError as err: ret["result"] = False ret["comment"] = f"{err}" @@ -307,8 +450,37 @@ def sections_present(name, sections=None, separator="="): return ret -def sections_absent(name, sections=None, separator="="): +def sections_absent(name, sections=None, separator="=", encoding=None): """ + Remove sections from the ini file. All key/value pairs in the section will + also be removed. + + Args: + + name (str): + The path to the ini file + + sections (dict): + A dictionary of sections and key/value pairs that will be used to + update the ini file. Other sections and key/value pairs in the ini + file will be untouched unless ``strict: True`` is passed. + + separator (str): + The character used to separate keys and values. Standard ini files + use the "=" character. The default is ``=``. + + encoding (str): + A string value representing encoding of the target ini file. If + ``None`` is passed, it uses the system default which is likely + ``utf-8``. Default is ``None`` + + .. versionadded:: 3006.6 + + Returns: + dict: A dictionary containing list of changes made + + Example: + .. code-block:: yaml /home/saltminion/api-paste.ini: @@ -317,9 +489,6 @@ def sections_absent(name, sections=None, separator="="): - sections: - test - test1 - - options present in file and not specified in sections will be deleted - changes dict will contain the sections that changed """ ret = { "name": name, @@ -331,7 +500,9 @@ def sections_absent(name, sections=None, separator="="): ret["result"] = True ret["comment"] = "" try: - cur_ini = __salt__["ini.get_ini"](name, separator) + cur_ini = __salt__["ini.get_ini"]( + file_name=name, separator=separator, encoding=encoding + ) except OSError as err: ret["result"] = False ret["comment"] = f"{err}" @@ -347,7 +518,9 @@ def sections_absent(name, sections=None, separator="="): return ret for section in sections or []: try: - cur_section = __salt__["ini.remove_section"](name, section, separator) + cur_section = __salt__["ini.remove_section"]( + file_name=name, section=section, separator=separator, encoding=encoding + ) except OSError as err: ret["result"] = False ret["comment"] = f"{err}" diff --git a/salt/states/virtualenv_mod.py b/salt/states/virtualenv_mod.py index 7dadfa23fd5..ceb25effb66 100644 --- a/salt/states/virtualenv_mod.py +++ b/salt/states/virtualenv_mod.py @@ -137,8 +137,8 @@ def managed( Current versions of Salt use onedir packages and will use onedir python interpreter by default. If you've installed Salt via out package repository. You will likely want to provide the path to the interpreter - with wich you would like to be used to create the virtual envrionment. The - interperter can be specified by providing the `python` option. + with which you would like to be used to create the virtual environment. The + interpreter can be specified by providing the `python` option. """ ret = {"name": name, "result": True, "comment": "", "changes": {}} diff --git a/salt/utils/atomicfile.py b/salt/utils/atomicfile.py index 5dfffbb83c2..a3bf2346804 100644 --- a/salt/utils/atomicfile.py +++ b/salt/utils/atomicfile.py @@ -11,6 +11,7 @@ import sys import tempfile import time +import salt.utils.files import salt.utils.win_dacl CAN_RENAME_OPEN_FILE = False @@ -128,15 +129,19 @@ class _AtomicWFile: if self._fh.closed: return self._fh.close() - if os.path.isfile(self._filename): - if salt.utils.win_dacl.HAS_WIN32: + if salt.utils.win_dacl.HAS_WIN32: + if os.path.isfile(self._filename): salt.utils.win_dacl.copy_security( source=self._filename, target=self._tmp_filename ) - else: + else: + if os.path.isfile(self._filename): shutil.copymode(self._filename, self._tmp_filename) st = os.stat(self._filename) os.chown(self._tmp_filename, st.st_uid, st.st_gid) + else: + # chmod file to default mode based on umask + os.chmod(self._tmp_filename, 0o666 & ~salt.utils.files.get_umask()) atomic_rename(self._tmp_filename, self._filename) def __exit__(self, exc_type, exc_value, traceback): diff --git a/salt/utils/gitfs.py b/salt/utils/gitfs.py index 9feda7a1677..a11efb1417d 100644 --- a/salt/utils/gitfs.py +++ b/salt/utils/gitfs.py @@ -1804,7 +1804,7 @@ class Pygit2(GitProvider): # remote ref. self.repo.checkout(checkout_ref) if branch: - self.repo.reset(oid, pygit2.GIT_RESET_HARD) + self.repo.reset(pygit2_id, pygit2.GIT_RESET_HARD) return True except GitLockError as exc: if exc.errno == errno.EEXIST: @@ -1833,11 +1833,11 @@ class Pygit2(GitProvider): tag_ref = "refs/tags/" + tgt_ref if remote_ref in refs: # Get commit id for the remote ref - oid = self.peel(self.repo.lookup_reference(remote_ref)).id + pygit2_id = self.peel(self.repo.lookup_reference(remote_ref)).id if local_ref not in refs: # No local branch for this remote, so create one and point # it at the commit id of the remote ref - self.repo.create_reference(local_ref, oid) + self.repo.create_reference(local_ref, pygit2_id) try: target_sha = self.peel(self.repo.lookup_reference(remote_ref)).hex @@ -1868,7 +1868,8 @@ class Pygit2(GitProvider): # cachedir). head_ref = local_head.target # If head_ref is not a string, it will point to a - # pygit2.Oid object and we are in detached HEAD mode. + # pygit2.id object (oid is deprecated and removed) and + # we are in detached HEAD mode. # Therefore, there is no need to add a local reference. If # head_ref == local_ref, then the local reference for HEAD # in refs/heads/ already exists and again, no need to add. @@ -2037,10 +2038,10 @@ class Pygit2(GitProvider): the empty directories within it in the "blobs" list """ for entry in iter(tree): - if entry.oid not in self.repo: + if entry.id not in self.repo: # Entry is a submodule, skip it continue - blob = self.repo[entry.oid] + blob = self.repo[entry.id] if not isinstance(blob, pygit2.Tree): continue blobs.append( @@ -2059,8 +2060,8 @@ class Pygit2(GitProvider): return ret if self.root(tgt_env): try: - oid = tree[self.root(tgt_env)].oid - tree = self.repo[oid] + pygit2_id = tree[self.root(tgt_env)].id + tree = self.repo[pygit2_id] except KeyError: return ret if not isinstance(tree, pygit2.Tree): @@ -2180,17 +2181,17 @@ class Pygit2(GitProvider): the file paths and symlink info in the "blobs" dict """ for entry in iter(tree): - if entry.oid not in self.repo: + if entry.id not in self.repo: # Entry is a submodule, skip it continue - obj = self.repo[entry.oid] + obj = self.repo[entry.id] if isinstance(obj, pygit2.Blob): repo_path = salt.utils.path.join( prefix, entry.name, use_posixpath=True ) blobs.setdefault("files", []).append(repo_path) if stat.S_ISLNK(tree[entry.name].filemode): - link_tgt = self.repo[tree[entry.name].oid].data + link_tgt = self.repo[tree[entry.name].id].data blobs.setdefault("symlinks", {})[repo_path] = link_tgt elif isinstance(obj, pygit2.Tree): _traverse( @@ -2209,8 +2210,8 @@ class Pygit2(GitProvider): try: # This might need to be changed to account for a root that # spans more than one directory - oid = tree[self.root(tgt_env)].oid - tree = self.repo[oid] + pygit2_id = tree[self.root(tgt_env)].id + tree = self.repo[pygit2_id] except KeyError: return files, symlinks if not isinstance(tree, pygit2.Tree): @@ -2263,12 +2264,12 @@ class Pygit2(GitProvider): # path's object ID will be the target of the symlink. Follow # the symlink and set path to the location indicated # in the blob data. - link_tgt = self.repo[entry.oid].data + link_tgt = self.repo[entry.id].data path = salt.utils.path.join( os.path.dirname(path), link_tgt, use_posixpath=True ) else: - blob = self.repo[entry.oid] + blob = self.repo[entry.id] if isinstance(blob, pygit2.Tree): # Path is a directory, not a file. blob = None diff --git a/salt/utils/platform.py b/salt/utils/platform.py index 100918b72d5..59a04b451bc 100644 --- a/salt/utils/platform.py +++ b/salt/utils/platform.py @@ -228,7 +228,11 @@ def is_aarch64(): """ Simple function to return if host is AArch64 or not """ - return platform.machine().startswith("aarch64") + if is_darwin(): + # Allow for MacOS Arm64 platform returning differently from Linux + return platform.machine().startswith("arm64") + else: + return platform.machine().startswith("aarch64") def spawning_platform(): diff --git a/salt/utils/process.py b/salt/utils/process.py index cdda5df02a5..3aa43e76ce6 100644 --- a/salt/utils/process.py +++ b/salt/utils/process.py @@ -1169,10 +1169,17 @@ class SubprocessList: def cleanup(self): with self.lock: - for proc in self.processes: - if proc.is_alive(): - continue - proc.join() + for proc in self.processes[:]: + proc.join(0.01) + if hasattr(proc, "exitcode"): + # Only processes have exitcode and a close method, threads + # do not. + if proc.exitcode is None: + continue + proc.close() + else: + if proc.is_alive(): + continue self.processes.remove(proc) self.count -= 1 log.debug("Subprocess %s cleaned up", proc.name) diff --git a/salt/utils/verify.py b/salt/utils/verify.py index 85d9e568390..947102135ad 100644 --- a/salt/utils/verify.py +++ b/salt/utils/verify.py @@ -532,7 +532,7 @@ def clean_path(root, path, subdir=False, realpath=True): Pass realpath=False if filesystem links should not be resolved. """ if not os.path.isabs(root): - return "" + root = os.path.join(os.getcwd(), root) root = os.path.normpath(root) if not os.path.isabs(path): path = os.path.join(root, path) diff --git a/salt/utils/win_update.py b/salt/utils/win_update.py index dd54f213963..1c93c5b18ea 100644 --- a/salt/utils/win_update.py +++ b/salt/utils/win_update.py @@ -528,14 +528,18 @@ class WindowsUpdateAgent: found = updates.updates for update in self._updates: + # Some update objects seem to be empty or undefined. Those will be + # exposed here if they are missing these attributes + try: + if salt.utils.data.is_true(update.IsHidden) and skip_hidden: + continue - if salt.utils.data.is_true(update.IsHidden) and skip_hidden: - continue + if salt.utils.data.is_true(update.IsInstalled) and skip_installed: + continue - if salt.utils.data.is_true(update.IsInstalled) and skip_installed: - continue - - if salt.utils.data.is_true(update.IsMandatory) and skip_mandatory: + if salt.utils.data.is_true(update.IsMandatory) and skip_mandatory: + continue + except AttributeError: continue # Windows 10 build 2004 introduced some problems with the diff --git a/tests/filename_map.yml b/tests/filename_map.yml index 932a70feb8d..810eac62fb2 100644 --- a/tests/filename_map.yml +++ b/tests/filename_map.yml @@ -224,8 +224,8 @@ salt/config/*: - pytests.unit.config.test__validate_opts salt/loader/*: - - integration.loader.test_ext_grains - integration.loader.test_ext_modules + - pytests.integration.loader.test_ext_grains - pytests.functional.loader.test_loader - pytests.functional.loader.test_loaded_base_name diff --git a/tests/integration/loader/test_ext_grains.py b/tests/integration/loader/test_ext_grains.py deleted file mode 100644 index b1c36ace68e..00000000000 --- a/tests/integration/loader/test_ext_grains.py +++ /dev/null @@ -1,86 +0,0 @@ -""" - integration.loader.ext_grains - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Test Salt's loader regarding external grains -""" - -import os -import time - -import pytest - -import salt.config -import salt.loader -from tests.support.case import ModuleCase -from tests.support.runtests import RUNTIME_VARS - - -@pytest.mark.skip_on_photonos( - reason="Consistant failures on photon, test needs refactoring" -) -@pytest.mark.windows_whitelisted -class LoaderGrainsTest(ModuleCase): - """ - Test the loader standard behavior with external grains - """ - - # def setUp(self): - # self.opts = minion_config(None) - # self.opts['disable_modules'] = ['pillar'] - # self.opts['grains'] = grains(self.opts) - - @pytest.mark.slow_test - def test_grains_overwrite(self): - # Force a grains sync - self.run_function("saltutil.sync_grains") - # To avoid a race condition on Windows, we need to make sure the - # `test_custom_grain2.py` file is present in the _grains directory - # before trying to get the grains. This test may execute before the - # minion has finished syncing down the files it needs. - module = os.path.join( - RUNTIME_VARS.RUNTIME_CONFIGS["minion"]["cachedir"], - "files", - "base", - "_grains", - "custom_grain2.py", - ) - tries = 0 - while not os.path.exists(module): - tries += 1 - if tries > 60: - self.fail( - "Failed to found custom grains module in cache path {}".format( - module - ) - ) - break - time.sleep(1) - grains = self.run_function("grains.items") - - # Check that custom grains are overwritten - self.assertEqual({"k2": "v2"}, grains["a_custom"]) - - -@pytest.mark.skip(reason="needs a way to reload minion after config change") -@pytest.mark.windows_whitelisted -class LoaderGrainsMergeTest(ModuleCase): - """ - Test the loader deep merge behavior with external grains - """ - - def setUp(self): - # XXX: This seems like it should become a unit test instead - self.opts = salt.config.minion_config(None) - self.opts["grains_deep_merge"] = True - self.assertTrue(self.opts["grains_deep_merge"]) - self.opts["disable_modules"] = ["pillar"] - __grains__ = salt.loader.grains(self.opts) - - def test_grains_merge(self): - __grain__ = self.run_function("grains.item", ["a_custom"]) - - # Check that the grain is present - self.assertIn("a_custom", __grain__) - # Check that the grains are merged - self.assertEqual({"k1": "v1", "k2": "v2"}, __grain__["a_custom"]) diff --git a/tests/pytests/functional/states/cmd/test_cmd_run.py b/tests/pytests/functional/states/cmd/test_cmd_run.py new file mode 100644 index 00000000000..22a5ba65698 --- /dev/null +++ b/tests/pytests/functional/states/cmd/test_cmd_run.py @@ -0,0 +1,64 @@ +import os + +import pytest + +import salt.utils.path + +pytestmark = [ + pytest.mark.windows_whitelisted, + pytest.mark.skip_unless_on_windows, + pytest.mark.destructive_test, + pytest.mark.slow_test, +] + + +@pytest.fixture(params=["powershell", "pwsh"]) +def shell(request): + """ + This will run the test on powershell and powershell core (pwsh). If + powershell core is not installed that test run will be skipped + """ + + if request.param == "pwsh" and salt.utils.path.which("pwsh") is None: + pytest.skip("Powershell 7 Not Present") + return request.param + + +def test_cmd_run_unless_true(shell, cmd): + # We need a directory that we know exists that has stuff in it + win_dir = os.getenv("WINDIR") + ret = cmd.run(name="echo foo", unless=f"ls {win_dir}", shell=shell) + assert ret.filtered["result"] is True + assert ret.filtered["name"] == "echo foo" + assert ret.filtered["comment"] == "unless condition is true" + assert ret.filtered["changes"] == {} + + +def test_cmd_run_unless_false(shell, cmd): + # We need a directory that we know does not exist + win_dir = "C:\\This\\Dir\\Does\\Not\\Exist" + ret = cmd.run(name="echo foo", unless=f"ls {win_dir}", shell=shell) + assert ret.filtered["result"] is True + assert ret.filtered["name"] == "echo foo" + assert ret.filtered["comment"] == 'Command "echo foo" run' + assert ret.filtered["changes"]["stdout"] == "foo" + + +def test_cmd_run_onlyif_true(shell, cmd): + # We need a directory that we know exists that has stuff in it + win_dir = os.getenv("WINDIR") + ret = cmd.run(name="echo foo", onlyif=f"ls {win_dir}", shell=shell) + assert ret.filtered["result"] is True + assert ret.filtered["name"] == "echo foo" + assert ret.filtered["comment"] == 'Command "echo foo" run' + assert ret.filtered["changes"]["stdout"] == "foo" + + +def test_cmd_run_onlyif_false(shell, cmd): + # We need a directory that we know does not exist + win_dir = "C:\\This\\Dir\\Does\\Not\\Exist" + ret = cmd.run(name="echo foo", onlyif=f"ls {win_dir}", shell=shell) + assert ret.filtered["result"] is True + assert ret.filtered["name"] == "echo foo" + assert ret.filtered["comment"] == "onlyif condition is false" + assert ret.filtered["changes"] == {} diff --git a/tests/pytests/functional/states/test_pkg.py b/tests/pytests/functional/states/test_pkg.py index 2b60663bb8e..158fd065b44 100644 --- a/tests/pytests/functional/states/test_pkg.py +++ b/tests/pytests/functional/states/test_pkg.py @@ -4,6 +4,7 @@ tests for pkg state import logging import os +import subprocess import time import pytest @@ -38,6 +39,16 @@ def refresh_db(grains, modules): pytest.fail("Package database locked after 60 seconds, bailing out") +@pytest.fixture(scope="module", autouse=True) +def refresh_keys(grains, modules): + if grains["os_family"] == "Arch": + # We should be running this periodically when building new test runner + # images, otherwise this could take several minuets to complete. + proc = subprocess.run(["pacman-key", "--refresh-keys"], check=False) + if proc.returncode != 0: + pytest.fail("pacman-key --refresh-keys command failed.") + + @pytest.fixture def PKG_TARGETS(grains): _PKG_TARGETS = ["figlet", "sl"] diff --git a/tests/pytests/functional/utils/test_process.py b/tests/pytests/functional/utils/test_process.py index ed165ea3e91..14525c426af 100644 --- a/tests/pytests/functional/utils/test_process.py +++ b/tests/pytests/functional/utils/test_process.py @@ -5,6 +5,10 @@ tests.pytests.functional.utils.test_process Test salt's process utility module """ +import os +import pathlib +import time + import pytest import salt.utils.process @@ -35,3 +39,35 @@ def test_process_manager_60749(process_manager): process_manager.add_process(Process) process_manager.check_children() + + +def _get_num_fds(pid): + "Determine the number of open fds for a process, linux only." + return len(list(pathlib.Path(f"/proc/{pid}/fd").iterdir())) + + +@pytest.mark.skip_unless_on_linux +def test_subprocess_list_fds(): + pid = os.getpid() + process_list = salt.utils.process.SubprocessList() + + before_num = _get_num_fds(pid) + + def target(): + pass + + process = salt.utils.process.SignalHandlingProcess(target=target) + process.start() + + process_list.add(process) + time.sleep(0.3) + + num = _get_num_fds(pid) + assert num == before_num + 2 + start = time.time() + while time.time() - start < 1: + process_list.cleanup() + if not process_list.processes: + break + assert len(process_list.processes) == 0 + assert _get_num_fds(pid) == num - 2 diff --git a/tests/pytests/integration/cli/test_salt.py b/tests/pytests/integration/cli/test_salt.py index 37925160ca6..90e3eed6d78 100644 --- a/tests/pytests/integration/cli/test_salt.py +++ b/tests/pytests/integration/cli/test_salt.py @@ -2,6 +2,7 @@ :codeauthor: Thayne Harbaugh (tharbaug@adobe.com) """ +import glob import logging import os import shutil @@ -276,3 +277,28 @@ def test_minion_65400(salt_cli, salt_minion, salt_minion_2, salt_master): for minion_id in ret.data: assert ret.data[minion_id] != "Error: test.configurable_test_state" assert isinstance(ret.data[minion_id], dict) + + +@pytest.mark.skip_on_windows(reason="Windows does not support SIGUSR1") +def test_sigusr1_handler(salt_master, salt_minion): + """ + Ensure SIGUSR1 handler works. + + Refer to https://docs.saltproject.io/en/latest/topics/troubleshooting/minion.html#live-python-debug-output for more details. + """ + tb_glob = os.path.join(tempfile.gettempdir(), "salt-debug-*.log") + tracebacks_before = glob.glob(tb_glob) + os.kill(salt_minion.pid, signal.SIGUSR1) + for i in range(10): + if len(glob.glob(tb_glob)) - len(tracebacks_before) == 1: + break + time.sleep(1) + + os.kill(salt_master.pid, signal.SIGUSR1) + for i in range(10): + if len(glob.glob(tb_glob)) - len(tracebacks_before) == 2: + break + time.sleep(1) + + tracebacks_after = glob.glob(tb_glob) + assert len(tracebacks_after) - len(tracebacks_before) == 2 diff --git a/tests/pytests/integration/cli/test_salt_call.py b/tests/pytests/integration/cli/test_salt_call.py index 1d770c0ffbe..f927f499c85 100644 --- a/tests/pytests/integration/cli/test_salt_call.py +++ b/tests/pytests/integration/cli/test_salt_call.py @@ -1,18 +1,24 @@ import copy import logging import os +import pathlib import pprint import re +import shutil import sys import pytest +from saltfactories.utils import random_string import salt.defaults.exitcodes import salt.utils.files import salt.utils.json import salt.utils.platform import salt.utils.yaml -from tests.support.helpers import PRE_PYTEST_SKIP, PRE_PYTEST_SKIP_REASON +import tests.conftest +import tests.support.helpers +from tests.conftest import FIPS_TESTRUN +from tests.support.runtests import RUNTIME_VARS pytestmark = [ pytest.mark.core_test, @@ -95,7 +101,7 @@ def test_local_salt_call(salt_call_cli): assert contents.count("foo") == 1, contents -@pytest.mark.skip_on_windows(reason=PRE_PYTEST_SKIP_REASON) +@pytest.mark.skip_on_windows(reason=tests.support.helpers.PRE_PYTEST_SKIP_REASON) def test_user_delete_kw_output(salt_call_cli): ret = salt_call_cli.run("-d", "user.delete", _timeout=120) assert ret.returncode == 0 @@ -126,7 +132,7 @@ def test_issue_6973_state_highstate_exit_code(salt_call_cli): assert expected_comment in ret.stdout -@PRE_PYTEST_SKIP +@tests.support.helpers.PRE_PYTEST_SKIP def test_issue_15074_output_file_append(salt_call_cli): with pytest.helpers.temp_file(name="issue-15074") as output_file_append: @@ -154,7 +160,7 @@ def test_issue_15074_output_file_append(salt_call_cli): assert second_run_output == first_run_output + first_run_output -@PRE_PYTEST_SKIP +@tests.support.helpers.PRE_PYTEST_SKIP def test_issue_14979_output_file_permissions(salt_call_cli): with pytest.helpers.temp_file(name="issue-14979") as output_file: with salt.utils.files.set_umask(0o077): @@ -307,7 +313,7 @@ def test_syslog_file_not_found(salt_minion, salt_call_cli, tmp_path): assert "Failed to setup the Syslog logging handler" in ret.stderr -@PRE_PYTEST_SKIP +@tests.support.helpers.PRE_PYTEST_SKIP @pytest.mark.skip_on_windows def test_return(salt_call_cli, salt_run_cli): command = "echo returnTOmaster" @@ -429,3 +435,99 @@ def test_local_salt_call_no_function_no_retcode(salt_call_cli): assert "test" in ret.data assert ret.data["test"] == "'test' is not available." assert "test.echo" in ret.data + + +@pytest.fixture +def master_id_alt(): + master_id = random_string("master-") + yield master_id + + +@pytest.fixture +def minion_id_alt(): + master_id = random_string("minion-") + yield master_id + + +@pytest.fixture +def salt_master_alt(salt_factories, tmp_path, master_id_alt): + """ + A running salt-master fixture + """ + root_dir = salt_factories.get_root_dir_for_daemon(master_id_alt) + conf_dir = root_dir / "conf" + conf_dir.mkdir(exist_ok=True) + extension_modules_path = str(root_dir / "extension_modules") + if not os.path.exists(extension_modules_path): + shutil.copytree( + os.path.join(RUNTIME_VARS.FILES, "extension_modules"), + extension_modules_path, + ) + cache = pathlib.Path(extension_modules_path) / "cache" + cache.mkdir() + localfs = cache / "localfs.py" + localfs.write_text( + tests.support.helpers.dedent( + """ + from salt.exceptions import SaltClientError + def store(bank, key, data): # , cachedir): + raise SaltClientError("TEST") + """ + ) + ) + factory = salt_factories.salt_master_daemon( + master_id_alt, + defaults={ + "root_dir": str(root_dir), + "extension_modules": extension_modules_path, + "auto_accept": True, + }, + overrides={ + "fips_mode": FIPS_TESTRUN, + "publish_signing_algorithm": ( + "PKCS1v15-SHA224" if FIPS_TESTRUN else "PKCS1v15-SHA1" + ), + }, + ) + with factory.pillar_tree.base.temp_file("cve_2024_37088.sls", "foobar: bang"): + with factory.state_tree.base.temp_file( + "cve_2024_37088.sls", + """ + # cvs_2024_37088.sls + {{%- set var = salt ['pillar.get']('foobar', 'state default') %}} + + test: + file.managed: + - name: {0} + - contents: {{{{ var }}}} + """.format( + tmp_path / "cve_2024_37088.txt" + ), + ): + with factory.started(): + yield factory + + +@pytest.fixture +def salt_call_alt(salt_master_alt, minion_id_alt): + minion_factory = salt_master_alt.salt_minion_daemon( + minion_id_alt, + overrides={ + "fips_mode": tests.conftest.FIPS_TESTRUN, + "encryption_algorithm": ( + "OAEP-SHA224" if tests.conftest.FIPS_TESTRUN else "OAEP-SHA1" + ), + "signing_algorithm": ( + "PKCS1v15-SHA224" if tests.conftest.FIPS_TESTRUN else "PKCS1v15-SHA1" + ), + }, + ) + return minion_factory.salt_call_cli() + + +def test_cve_2024_37088(salt_master_alt, salt_call_alt, caplog): + with caplog.at_level(logging.ERROR): + ret = salt_call_alt.run("state.sls", "cve_2024_37088") + assert ret.returncode == 1 + assert ret.data is None + assert "Got a bad pillar from master, type str, expecting dict" in caplog.text diff --git a/tests/pytests/integration/loader/__init__.py b/tests/pytests/integration/loader/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/pytests/integration/loader/test_ext_grains.py b/tests/pytests/integration/loader/test_ext_grains.py new file mode 100644 index 00000000000..227859db03f --- /dev/null +++ b/tests/pytests/integration/loader/test_ext_grains.py @@ -0,0 +1,53 @@ +import pytest + +from tests.conftest import FIPS_TESTRUN + + +def test_grains_overwrite(salt_cli, salt_master, salt_minion): + assert not salt_minion.config.get("grains_deep_merge", False) + # Force a grains sync + salt_cli.run("saltutil.sync_grains", minion_tgt=salt_minion.id) + + # XXX: This should no longer be neede because of using salt_cli.run. + # To avoid a race condition on Windows, we need to make sure the + # `test_custom_grain2.py` file is present in the _grains directory + # before trying to get the grains. This test may execute before the + # minion has finished syncing down the files it needs. + # module = os.path.join( + # salt_minion.config["cachedir"], + # "files", + # "base", + # "_grains", + # "custom_grain2.py", + # ) + # assert os.path.exists(module) + + # Check that custom grains are overwritten + ret = salt_cli.run("grains.items", minion_tgt=salt_minion.id) + assert ret.data["a_custom"] == {"k2": "v2"} + + +def test_grains_merge(salt_cli, salt_master): + minion = salt_master.salt_minion_daemon( + "test_grains_merge", + overrides={ + "grains_deep_merge": True, + # Grains in the minon config won't get merged. + # "grains": {"a_custom": {"k1": "v1"}}, + "fips_mode": FIPS_TESTRUN, + "encryption_algorithm": "OAEP-SHA224" if FIPS_TESTRUN else "OAEP-SHA1", + "signing_algorithm": "PKCS1v15-SHA224" if FIPS_TESTRUN else "PKCS1v15-SHA1", + }, + ) + minion.after_terminate( + pytest.helpers.remove_stale_minion_key, salt_master, minion.id + ) + content = """ + def grain(): + return {"a_custom": {"k1": "v1"}} + """ + with salt_master.state_tree.base.temp_file("_grains/tempcustom.py", content): + with minion.started(): + salt_cli.run("saltutil.sync_grains", minion_tgt=minion.id) + ret = salt_cli.run("grains.item", "a_custom", minion_tgt=minion.id) + assert ret.data["a_custom"] == {"k1": "v1", "k2": "v2"} diff --git a/tests/pytests/integration/ssh/state/test_parallel.py b/tests/pytests/integration/ssh/state/test_parallel.py new file mode 100644 index 00000000000..8ff9d7db139 --- /dev/null +++ b/tests/pytests/integration/ssh/state/test_parallel.py @@ -0,0 +1,61 @@ +""" +Verify salt-ssh states support ``parallel``. +""" + +import pytest + +pytestmark = [ + pytest.mark.skip_on_windows(reason="salt-ssh not available on Windows"), + pytest.mark.slow_test, +] + + +@pytest.fixture(scope="module", autouse=True) +def state_tree_parallel(base_env_state_tree_root_dir): + top_file = """ + base: + 'localhost': + - parallel + '127.0.0.1': + - parallel + """ + state_file = """ + {%- for i in range(5) %} + This runs in parallel {{ i }}: + cmd.run: + - name: sleep 0.{{ i }} + - parallel: true + {%- endfor %} + """ + top_tempfile = pytest.helpers.temp_file( + "top.sls", top_file, base_env_state_tree_root_dir + ) + state_tempfile = pytest.helpers.temp_file( + "parallel.sls", state_file, base_env_state_tree_root_dir + ) + with top_tempfile, state_tempfile: + yield + + +@pytest.mark.parametrize( + "args", + ( + pytest.param(("state.sls", "parallel"), id="sls"), + pytest.param(("state.highstate",), id="highstate"), + pytest.param(("state.top", "top.sls"), id="top"), + ), +) +def test_it(salt_ssh_cli, args): + """ + Ensure states with ``parallel: true`` do not cause a crash. + This does not check that they were actually run in parallel + since that would result either in a long-running or flaky test. + """ + ret = salt_ssh_cli.run(*args) + assert ret.returncode == 0 + assert isinstance(ret.data, dict) + for i in range(5): + key = f"cmd_|-This runs in parallel {i}_|-sleep 0.{i}_|-run" + assert key in ret.data + assert "pid" in ret.data[key]["changes"] + assert ret.data[key]["changes"]["retcode"] == 0 diff --git a/tests/pytests/pkg/conftest.py b/tests/pytests/pkg/conftest.py index 59d02c3a60d..258d794c89f 100644 --- a/tests/pytests/pkg/conftest.py +++ b/tests/pytests/pkg/conftest.py @@ -90,12 +90,6 @@ def pytest_addoption(parser): action="store_true", help="Do not uninstall salt packages after test run is complete", ) - test_selection_group.addoption( - "--classic", - default=False, - action="store_true", - help="Test an upgrade from the classic packages.", - ) test_selection_group.addoption( "--prev-version", action="store", @@ -231,7 +225,6 @@ def install_salt(request, salt_factories_root_dir): downgrade=request.config.getoption("--downgrade"), no_uninstall=request.config.getoption("--no-uninstall"), no_install=request.config.getoption("--no-install"), - classic=request.config.getoption("--classic"), prev_version=request.config.getoption("--prev-version"), use_prev_version=request.config.getoption("--use-prev-version"), ) as fixture: @@ -357,18 +350,7 @@ def salt_master(salt_factories, install_salt, pkg_tests_account): master_script = False if platform.is_windows(): - if install_salt.classic: - master_script = True - if install_salt.relenv: - master_script = True - elif not install_salt.upgrade: - master_script = True - if ( - not install_salt.relenv - and install_salt.use_prev_version - and not install_salt.classic - ): - master_script = False + master_script = True if master_script: salt_factories.system_service = False @@ -376,11 +358,7 @@ def salt_master(salt_factories, install_salt, pkg_tests_account): scripts_dir = salt_factories.root_dir / "Scripts" scripts_dir.mkdir(exist_ok=True) salt_factories.scripts_dir = scripts_dir - python_executable = install_salt.bin_dir / "Scripts" / "python.exe" - if install_salt.classic: - python_executable = install_salt.bin_dir / "python.exe" - if install_salt.relenv: - python_executable = install_salt.install_dir / "Scripts" / "python.exe" + python_executable = install_salt.install_dir / "Scripts" / "python.exe" salt_factories.python_executable = python_executable factory = salt_factories.salt_master_daemon( random_string("master-"), @@ -391,10 +369,6 @@ def salt_master(salt_factories, install_salt, pkg_tests_account): ) salt_factories.system_service = True else: - - if install_salt.classic and platform.is_darwin(): - os.environ["PATH"] += ":/opt/salt/bin" - factory = salt_factories.salt_master_daemon( random_string("master-"), defaults=config_defaults, @@ -465,12 +439,6 @@ def salt_minion(salt_factories, salt_master, install_salt): ) config_overrides["winrepo_source_dir"] = r"salt://win/repo_ng" - if install_salt.classic and platform.is_windows(): - salt_factories.python_executable = None - - if install_salt.classic and platform.is_darwin(): - os.environ["PATH"] += ":/opt/salt/bin" - factory = salt_master.salt_minion_daemon( minion_id, overrides=config_overrides, diff --git a/tests/pytests/pkg/downgrade/test_salt_downgrade.py b/tests/pytests/pkg/downgrade/test_salt_downgrade.py index adba7c51272..a195bb880c7 100644 --- a/tests/pytests/pkg/downgrade/test_salt_downgrade.py +++ b/tests/pytests/pkg/downgrade/test_salt_downgrade.py @@ -1,12 +1,44 @@ +import time + import packaging.version import psutil +import pytest from pytestskipmarkers.utils import platform -def test_salt_downgrade(salt_call_cli, install_salt): +def _get_running_named_salt_pid(process_name): + + # need to check all of command line for salt-minion, salt-master, for example: salt-minion + # + # Linux: psutil process name only returning first part of the command '/opt/saltstack/' + # Linux: ['/opt/saltstack/salt/bin/python3.10 /usr/bin/salt-minion MultiMinionProcessManager MinionProcessManager'] + # + # MacOS: psutil process name only returning last part of the command '/opt/salt/bin/python3.10', that is 'python3.10' + # MacOS: ['/opt/salt/bin/python3.10 /opt/salt/salt-minion', ''] + + pids = [] + for proc in psutil.process_iter(): + cmdl_strg = " ".join(str(element) for element in proc.cmdline()) + if process_name in cmdl_strg: + pids.append(proc.pid) + + return pids + + +def test_salt_downgrade_minion(salt_call_cli, install_salt): """ - Test an upgrade of Salt. + Test an downgrade of Salt Minion. """ + is_restart_fixed = packaging.version.parse( + install_salt.prev_version + ) < packaging.version.parse("3006.9") + + if is_restart_fixed and install_salt.distro_id in ("ubuntu", "debian", "darwin"): + pytest.skip( + "Skip package test, since downgrade version is less than " + "3006.9 which had fixes for salt-minion restarting, see PR 66218" + ) + is_downgrade_to_relenv = packaging.version.parse( install_salt.prev_version ) >= packaging.version.parse("3006.0") @@ -15,7 +47,7 @@ def test_salt_downgrade(salt_call_cli, install_salt): original_py_version = install_salt.package_python_version() # Verify current install version is setup correctly and works - ret = salt_call_cli.run("test.version") + ret = salt_call_cli.run("--local", "test.version") assert ret.returncode == 0 assert packaging.version.parse(ret.data) == packaging.version.parse( install_salt.artifact_version @@ -38,42 +70,34 @@ def test_salt_downgrade(salt_call_cli, install_salt): else: process_name = "salt-minion" - old_pid = [] - - # psutil process name only returning first part of the command '/opt/saltstack/' - # need to check all of command line for salt-minion - # ['/opt/saltstack/salt/bin/python3.10 /usr/bin/salt-minion MultiMinionProcessManager MinionProcessManager'] - # and psutil is only returning the salt-minion once - for proc in psutil.process_iter(): - if salt_name in proc.name(): - cmdl_strg = " ".join(str(element) for element in proc.cmdline()) - if process_name in cmdl_strg: - old_pid.append(proc.pid) - - assert old_pid + old_minion_pids = _get_running_named_salt_pid(process_name) + assert old_minion_pids # Downgrade Salt to the previous version and test install_salt.install(downgrade=True) + + time.sleep(10) # give it some time + # downgrade install will stop services on Debian/Ubuntu + # This is due to RedHat systems are not active after an install, but Debian/Ubuntu are active after an install + # want to ensure our tests start with the config settings we have set, + # trying restart for Debian/Ubuntu to see the outcome + if install_salt.distro_id in ("ubuntu", "debian"): + install_salt.restart_services() + + time.sleep(60) # give it some time + + # Verify there is a new running minion by getting its PID and comparing it + # with the PID from before the upgrade + new_minion_pids = _get_running_named_salt_pid(process_name) + assert new_minion_pids + assert new_minion_pids != old_minion_pids + bin_file = "salt" if platform.is_windows(): if not is_downgrade_to_relenv: bin_file = install_salt.install_dir / "salt-call.bat" else: bin_file = install_salt.install_dir / "salt-call.exe" - elif platform.is_darwin() and install_salt.classic: - bin_file = install_salt.bin_dir / "salt-call" - - # Verify there is a new running minion by getting its PID and comparing it - # with the PID from before the upgrade - new_pid = [] - for proc in psutil.process_iter(): - if salt_name in proc.name(): - cmdl_strg = " ".join(str(element) for element in proc.cmdline()) - if process_name in cmdl_strg: - new_pid.append(proc.pid) - - assert new_pid - assert new_pid != old_pid ret = install_salt.proc.run(bin_file, "--version") assert ret.returncode == 0 @@ -81,7 +105,7 @@ def test_salt_downgrade(salt_call_cli, install_salt): ret.stdout.strip().split()[1] ) < packaging.version.parse(install_salt.artifact_version) - if is_downgrade_to_relenv: + if is_downgrade_to_relenv and not platform.is_darwin(): new_py_version = install_salt.package_python_version() if new_py_version == original_py_version: # test pip install after a downgrade diff --git a/tests/pytests/pkg/download/test_pkg_download.py b/tests/pytests/pkg/download/test_pkg_download.py index 9a0fbd76bad..a6f4c7e38e7 100644 --- a/tests/pytests/pkg/download/test_pkg_download.py +++ b/tests/pytests/pkg/download/test_pkg_download.py @@ -7,8 +7,10 @@ import logging import os import pathlib import shutil +import time import packaging.version +import psutil import pytest from pytestskipmarkers.utils import platform @@ -460,6 +462,7 @@ def setup_windows( repo_subpath, package_type, onedir_install_path, + timeout=300, ): try: arch = os.environ.get("SALT_REPO_ARCH") or "amd64" @@ -491,6 +494,45 @@ def setup_windows( pytest.helpers.download_file(win_pkg_url, pkg_path) if package_type.lower() == "nsis": + # We need to make sure there are no installer/uninstaller + # processes running. Uninst.exe launches a 2nd binary + # (Un.exe or Un_*.exe) Let's get the name of the process + processes = [ + win_pkg, + "uninst.exe", + "Un.exe", + "Un_A.exe", + "Un_B.exe", + "Un_C.exe", + "Un_D.exe", + "Un_D.exe", + "Un_F.exe", + "Un_G.exe", + ] + proc_name = "" + for proc in processes: + try: + if proc in (p.name() for p in psutil.process_iter()): + proc_name = proc + except psutil.NoSuchProcess: + continue + + # We need to give the process time to exit. We'll timeout after + # 5 minutes or whatever timeout is set to + if proc_name: + elapsed_time = 0 + while elapsed_time < timeout: + try: + if proc_name not in ( + p.name() for p in psutil.process_iter() + ): + break + except psutil.NoSuchProcess: + continue + elapsed_time += 0.1 + time.sleep(0.1) + + # Only run setup when we're sure no other installations are running ret = shell.run(str(pkg_path), "/start-minion=0", "/S", check=False) else: ret = shell.run( @@ -556,6 +598,7 @@ def salt_test_command(request, install_dir): return command +@pytest.mark.skip_on_windows(reason="This is flaky on Windows") @pytest.mark.parametrize("salt_test_command", get_salt_test_commands(), indirect=True) def test_download(shell, salt_test_command): """ diff --git a/tests/pytests/pkg/integration/test_clean_zmq_teardown.py b/tests/pytests/pkg/integration/test_clean_zmq_teardown.py index 309493e69aa..d1dbe325ab2 100644 --- a/tests/pytests/pkg/integration/test_clean_zmq_teardown.py +++ b/tests/pytests/pkg/integration/test_clean_zmq_teardown.py @@ -12,12 +12,6 @@ pytestmark = [ log = logging.getLogger(__name__) -@pytest.fixture(autouse=True) -def _skip_on_non_relenv(install_salt): - if not install_salt.relenv: - pytest.skip("This test is for relenv versions of salt") - - def test_check_no_import_error(salt_call_cli, salt_master): """ Test that we don't have any errors on teardown of python when using a py-rendered sls file diff --git a/tests/pytests/pkg/integration/test_enabled_disabled.py b/tests/pytests/pkg/integration/test_enabled_disabled.py index 99097b187ee..4cfa5d2adc1 100644 --- a/tests/pytests/pkg/integration/test_enabled_disabled.py +++ b/tests/pytests/pkg/integration/test_enabled_disabled.py @@ -2,8 +2,8 @@ import pytest from pytestskipmarkers.utils import platform -@pytest.mark.skip_on_windows(reason="Linux test only") -def test_services(install_salt, salt_cli, salt_minion): +@pytest.mark.skip_unless_on_linux(reason="Linux test only") +def test_services(install_salt, salt_call_cli): """ Check if Services are enabled/disabled """ @@ -29,9 +29,15 @@ def test_services(install_salt, salt_cli, salt_minion): pytest.fail(f"Don't know how to handle os_family={install_salt.distro_id}") for service in services_enabled: - ret = salt_cli.run("service.enabled", service, minion_tgt=salt_minion.id) - assert "true" in ret.stdout + test_cmd = f"systemctl show -p UnitFileState {service}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_enabled = ret.stdout.strip().split("=")[1].split('"')[0].strip() + assert ret.returncode == 0 + assert test_enabled == "enabled" for service in services_disabled: - ret = salt_cli.run("service.disabled", service, minion_tgt=salt_minion.id) - assert "true" in ret.stdout + test_cmd = f"systemctl show -p UnitFileState {service}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_enabled = ret.stdout.strip().split("=")[1].split('"')[0].strip() + assert ret.returncode == 0 + assert test_enabled == "disabled" diff --git a/tests/pytests/pkg/integration/test_pip.py b/tests/pytests/pkg/integration/test_pip.py index 849dbbbfb8b..3dde96ebb7d 100644 --- a/tests/pytests/pkg/integration/test_pip.py +++ b/tests/pytests/pkg/integration/test_pip.py @@ -74,8 +74,6 @@ def test_pip_install_extras(shell, install_salt, extras_pypath_bin): """ Test salt-pip installs into the correct directory """ - if not install_salt.relenv: - pytest.skip("The extras directory is only in relenv versions") dep = "pep8" extras_keyword = "extras-3" if platform.is_windows(): @@ -125,11 +123,7 @@ def test_pip_non_root( pypath, pkg_tests_account_environ, ): - if install_salt.classic: - pytest.skip("We can install non-root for classic packages") check_path = extras_pypath_bin / "pep8" - if not install_salt.relenv and not install_salt.classic: - check_path = pypath / "pep8" # We should be able to issue a --help without being root ret = subprocess.run( install_salt.binary_paths["salt"] + ["--help"], @@ -179,8 +173,6 @@ def test_pip_install_salt_extension_in_extras(install_salt, extras_pypath, shell Test salt-pip installs into the correct directory and the salt extension is properly loaded. """ - if not install_salt.relenv: - pytest.skip("The extras directory is only in relenv versions") dep = "salt-analytics-framework" dep_version = "0.1.0" diff --git a/tests/pytests/pkg/integration/test_python.py b/tests/pytests/pkg/integration/test_python.py index 9b16cea3796..77d2a82a16c 100644 --- a/tests/pytests/pkg/integration/test_python.py +++ b/tests/pytests/pkg/integration/test_python.py @@ -6,9 +6,6 @@ import pytest @pytest.fixture def python_script_bin(install_salt): - # Tiamat builds run scripts via `salt python` - if not install_salt.relenv and not install_salt.classic: - return install_salt.binary_paths["python"][:1] + ["python"] return install_salt.binary_paths["python"] diff --git a/tests/pytests/pkg/integration/test_salt_api.py b/tests/pytests/pkg/integration/test_salt_api.py index 3ba7b74b62a..e962fbe3221 100644 --- a/tests/pytests/pkg/integration/test_salt_api.py +++ b/tests/pytests/pkg/integration/test_salt_api.py @@ -5,10 +5,17 @@ pytestmark = [ ] -def test_salt_api(api_request): +def test_salt_api(api_request, salt_master, install_salt): """ Test running a command against the salt api """ + if install_salt.distro_id in ("ubuntu", "debian"): + pytest.skip( + "Package test are getting reworked in https://github.com/saltstack/salt/issues/66672" + ) + + assert salt_master.is_running() + ret = api_request.post( "/run", data={ diff --git a/tests/pytests/pkg/integration/test_salt_call.py b/tests/pytests/pkg/integration/test_salt_call.py index 69f434a2c40..c16ecb67481 100644 --- a/tests/pytests/pkg/integration/test_salt_call.py +++ b/tests/pytests/pkg/integration/test_salt_call.py @@ -13,10 +13,12 @@ def test_salt_call_local(salt_call_cli): assert ret.data is True -def test_salt_call(salt_call_cli): +def test_salt_call(salt_call_cli, salt_master): """ Test salt-call test.ping """ + assert salt_master.is_running() + ret = salt_call_cli.run("test.ping") assert ret.returncode == 0 assert ret.data is True @@ -44,10 +46,12 @@ def state_name(salt_master): yield name -def test_sls(salt_call_cli, state_name): +def test_sls(salt_call_cli, salt_master, state_name): """ Test calling a sls file """ + assert salt_master.is_running() + ret = salt_call_cli.run("state.apply", state_name) assert ret.returncode == 0 assert ret.data diff --git a/tests/pytests/pkg/integration/test_salt_grains.py b/tests/pytests/pkg/integration/test_salt_grains.py index 422179350b9..071dcf1525e 100644 --- a/tests/pytests/pkg/integration/test_salt_grains.py +++ b/tests/pytests/pkg/integration/test_salt_grains.py @@ -6,37 +6,45 @@ pytestmark = [ ] -def test_grains_items(salt_cli, salt_minion): +def test_grains_items(salt_cli, salt_minion, salt_master): """ Test grains.items """ + assert salt_master.is_running() + ret = salt_cli.run("grains.items", minion_tgt=salt_minion.id) assert ret.data, ret assert "osrelease" in ret.data -def test_grains_item_os(salt_cli, salt_minion): +def test_grains_item_os(salt_cli, salt_minion, salt_master): """ Test grains.item os """ + assert salt_master.is_running() + ret = salt_cli.run("grains.item", "os", minion_tgt=salt_minion.id) assert ret.data, ret assert "os" in ret.data -def test_grains_item_pythonversion(salt_cli, salt_minion): +def test_grains_item_pythonversion(salt_cli, salt_minion, salt_master): """ Test grains.item pythonversion """ + assert salt_master.is_running() + ret = salt_cli.run("grains.item", "pythonversion", minion_tgt=salt_minion.id) assert ret.data, ret assert "pythonversion" in ret.data -def test_grains_setval_key_val(salt_cli, salt_minion): +def test_grains_setval_key_val(salt_cli, salt_minion, salt_master): """ Test grains.setval key val """ + assert salt_master.is_running() + ret = salt_cli.run("grains.setval", "key", "val", minion_tgt=salt_minion.id) assert ret.data, ret assert "key" in ret.data diff --git a/tests/pytests/pkg/integration/test_salt_minion.py b/tests/pytests/pkg/integration/test_salt_minion.py index b62de8d841e..1a06db1b1f3 100644 --- a/tests/pytests/pkg/integration/test_salt_minion.py +++ b/tests/pytests/pkg/integration/test_salt_minion.py @@ -5,20 +5,24 @@ pytestmark = [ ] -def test_salt_minion_ping(salt_cli, salt_minion): +def test_salt_minion_ping(salt_cli, salt_minion, salt_master): """ Test running a command against a targeted minion """ + assert salt_master.is_running() + ret = salt_cli.run("test.ping", minion_tgt=salt_minion.id) assert ret.returncode == 0 assert ret.data is True -def test_salt_minion_setproctitle(salt_cli, salt_minion): +def test_salt_minion_setproctitle(salt_cli, salt_minion, salt_master): """ Test that setproctitle is working for the running Salt minion """ + assert salt_master.is_running() + ret = salt_cli.run( "ps.pgrep", "MinionProcessManager", full=True, minion_tgt=salt_minion.id ) diff --git a/tests/pytests/pkg/integration/test_salt_output.py b/tests/pytests/pkg/integration/test_salt_output.py index e05cf457ded..b4d61044846 100644 --- a/tests/pytests/pkg/integration/test_salt_output.py +++ b/tests/pytests/pkg/integration/test_salt_output.py @@ -6,10 +6,12 @@ pytestmark = [ @pytest.mark.parametrize("output_fmt", ["yaml", "json"]) -def test_salt_output(salt_cli, salt_minion, output_fmt): +def test_salt_output(salt_cli, salt_minion, salt_master, output_fmt): """ Test --output """ + assert salt_master.is_running() + ret = salt_cli.run( f"--output={output_fmt}", "test.fib", "7", minion_tgt=salt_minion.id ) diff --git a/tests/pytests/pkg/integration/test_salt_pillar.py b/tests/pytests/pkg/integration/test_salt_pillar.py index f6cacf14b3c..7e1f98a3542 100644 --- a/tests/pytests/pkg/integration/test_salt_pillar.py +++ b/tests/pytests/pkg/integration/test_salt_pillar.py @@ -35,10 +35,12 @@ def pillar_name(salt_master): yield name -def test_salt_pillar(salt_cli, salt_minion, pillar_name): +def test_salt_pillar(salt_cli, salt_minion, salt_master, pillar_name): """ Test pillar.items """ + assert salt_master.is_running() + ret = salt_cli.run("pillar.items", minion_tgt=salt_minion.id) assert ret.returncode == 0 assert pillar_name in ret.data diff --git a/tests/pytests/pkg/integration/test_salt_state_file.py b/tests/pytests/pkg/integration/test_salt_state_file.py index 1aadf3dbddb..0c4804654cb 100644 --- a/tests/pytests/pkg/integration/test_salt_state_file.py +++ b/tests/pytests/pkg/integration/test_salt_state_file.py @@ -52,13 +52,14 @@ def state_name(files, salt_master): yield name -def test_salt_state_file(salt_cli, salt_minion, state_name, files): +def test_salt_state_file(salt_cli, salt_minion, salt_master, state_name, files): """ Test state file """ assert files.fpath_1.exists() is False assert files.fpath_2.exists() is False assert files.fpath_3.exists() is False + assert salt_master.is_running() ret = salt_cli.run("state.apply", state_name, minion_tgt=salt_minion.id) assert ret.returncode == 0 diff --git a/tests/pytests/pkg/integration/test_salt_ufw.py b/tests/pytests/pkg/integration/test_salt_ufw.py index 2164de85c57..0e0471aebf2 100644 --- a/tests/pytests/pkg/integration/test_salt_ufw.py +++ b/tests/pytests/pkg/integration/test_salt_ufw.py @@ -2,18 +2,45 @@ import pathlib import pytest +pytestmark = [ + pytest.mark.skip_unless_on_linux, +] + + +@pytest.fixture +def salt_systemd_setup( + salt_call_cli, + install_salt, +): + """ + Fixture to set systemd for salt packages to enabled and active + Note: assumes Salt packages already installed + """ + install_salt.install() + + # ensure known state, enabled and active + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + test_cmd = f"systemctl enable {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + assert ret.returncode == 0 + + test_cmd = f"systemctl restart {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + assert ret.returncode == 0 + -@pytest.mark.skip_on_windows @pytest.mark.skip_if_binaries_missing("ufw") -def test_salt_ufw(salt_master, salt_call_cli, install_salt): +def test_salt_ufw(salt_systemd_setup, salt_call_cli, install_salt): """ Test salt.ufw for Debian/Ubuntu salt-master """ if install_salt.distro_id not in ("debian", "ubuntu"): pytest.skip("Only tests Debian / Ubuntu packages") - # check that the salt_master is running - assert salt_master.is_running() + # setup systemd to enabled and active for Salt packages + # pylint: disable=pointless-statement + salt_systemd_setup ufw_master_path = pathlib.Path("/etc/ufw/applications.d/salt.ufw") assert ufw_master_path.exists() diff --git a/tests/pytests/pkg/integration/test_salt_user.py b/tests/pytests/pkg/integration/test_salt_user.py index 834fd399121..fb42ae3c9f6 100644 --- a/tests/pytests/pkg/integration/test_salt_user.py +++ b/tests/pytests/pkg/integration/test_salt_user.py @@ -9,18 +9,33 @@ import pytest from saltfactories.utils.tempfiles import temp_directory pytestmark = [ - pytest.mark.skip_on_windows, - pytest.mark.skip_on_darwin, - pytest.mark.skipif( - True, - reason=( - "Package permissions are getting reworked in " - "https://github.com/saltstack/salt/pull/66218" - ), - ), + pytest.mark.skip_unless_on_linux, ] +@pytest.fixture +def salt_systemd_setup( + salt_call_cli, + install_salt, +): + """ + Fixture to set systemd for salt packages to enabled and active + Note: assumes Salt packages already installed + """ + install_salt.install() + + # ensure known state, enabled and active + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + test_cmd = f"systemctl enable {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + assert ret.returncode == 0 + + test_cmd = f"systemctl restart {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + assert ret.returncode == 0 + + @pytest.fixture def pkg_paths(): """ @@ -68,16 +83,12 @@ def pkg_paths_salt_user_exclusions(): return paths -@pytest.fixture(autouse=True) -def _skip_on_non_relenv(install_salt): - if not install_salt.relenv: - pytest.skip("The salt user only exists on relenv versions of salt") - - def test_salt_user_master(salt_master, install_salt): """ Test the correct user is running the Salt Master """ + assert salt_master.is_running() + match = False for proc in psutil.Process(salt_master.pid).children(): assert proc.username() == "salt" @@ -86,10 +97,12 @@ def test_salt_user_master(salt_master, install_salt): assert match -def test_salt_user_home(install_salt): +def test_salt_user_home(install_salt, salt_master): """ Test the salt user's home is /opt/saltstack/salt """ + assert salt_master.is_running() + proc = subprocess.run( ["getent", "passwd", "salt"], check=False, capture_output=True ) @@ -102,10 +115,12 @@ def test_salt_user_home(install_salt): assert home == "/opt/saltstack/salt" -def test_salt_user_group(install_salt): +def test_salt_user_group(install_salt, salt_master): """ Test the salt user is in the salt group """ + assert salt_master.is_running() + proc = subprocess.run(["id", "salt"], check=False, capture_output=True) assert proc.returncode == 0 in_group = False @@ -118,10 +133,12 @@ def test_salt_user_group(install_salt): assert in_group is True -def test_salt_user_shell(install_salt): +def test_salt_user_shell(install_salt, salt_master): """ Test the salt user's login shell """ + assert salt_master.is_running() + proc = subprocess.run( ["getent", "passwd", "salt"], check=False, capture_output=True ) @@ -137,7 +154,10 @@ def test_salt_user_shell(install_salt): def test_pkg_paths( - install_salt, pkg_paths, pkg_paths_salt_user, pkg_paths_salt_user_exclusions + install_salt, + pkg_paths, + pkg_paths_salt_user, + pkg_paths_salt_user_exclusions, ): """ Test package paths ownership @@ -146,7 +166,9 @@ def test_pkg_paths( "3006.4" ): pytest.skip("Package path ownership was changed in salt 3006.4") + salt_user_subdirs = [] + for _path in pkg_paths: pkg_path = pathlib.Path(_path) assert pkg_path.exists() @@ -171,6 +193,8 @@ def test_pkg_paths( assert path.owner() == "root" assert path.group() == "root" for file in files: + if file.endswith("ipc"): + continue file_path = path.joinpath(file) # Individual files owned by salt user if str(file_path) in pkg_paths_salt_user: @@ -182,7 +206,11 @@ def test_pkg_paths( @pytest.mark.skip_if_binaries_missing("logrotate") def test_paths_log_rotation( - salt_master, salt_minion, salt_call_cli, install_salt, pkg_tests_account + salt_master, + salt_minion, + salt_call_cli, + install_salt, + pkg_tests_account, ): """ Test the correct ownership is assigned when log rotation occurs @@ -207,8 +235,6 @@ def test_paths_log_rotation( "Only tests RedHat family packages till logrotation paths are resolved on Ubuntu/Debian, see issue 65231" ) - # check that the salt_master is running - assert salt_master.is_running() match = False for proc in psutil.Process(salt_master.pid).children(): assert proc.username() == "salt" diff --git a/tests/pytests/pkg/integration/test_systemd_config.py b/tests/pytests/pkg/integration/test_systemd_config.py index 828e4413ad7..5f705eb2ee9 100644 --- a/tests/pytests/pkg/integration/test_systemd_config.py +++ b/tests/pytests/pkg/integration/test_systemd_config.py @@ -3,7 +3,7 @@ import subprocess import pytest pytestmark = [ - pytest.mark.skip_on_windows(reason="Linux test only"), + pytest.mark.skip_unless_on_linux, ] diff --git a/tests/pytests/pkg/integration/test_version.py b/tests/pytests/pkg/integration/test_version.py index 521e23973c2..6ef06f310e2 100644 --- a/tests/pytests/pkg/integration/test_version.py +++ b/tests/pytests/pkg/integration/test_version.py @@ -11,9 +11,25 @@ def test_salt_version(version, install_salt): """ Test version output from salt --version """ + actual = [] test_bin = os.path.join(*install_salt.binary_paths["salt"]) ret = install_salt.proc.run(test_bin, "--version") - actual = ret.stdout.strip().split(" ")[:2] + if "+" in version: + # testing a non-release build artifact version + actual = ret.stdout.strip().split(" ")[:2] + else: + # testing against release build version, for example: downgrade + actual_ver = ret.stdout.strip().split(" ")[:2] + actual_ver_salt = actual_ver[1] # get salt version + if "+" in actual_ver_salt: + actual_ver_salt_stripped = actual_ver_salt.split("+")[ + 0 + ] # strip any git versioning + actual.append(actual_ver[0]) + actual.append(actual_ver_salt_stripped) + else: + pytest.skip("Not testing a non-release build artifact, do not run") + expected = ["salt", version] assert actual == expected @@ -23,8 +39,6 @@ def test_salt_versions_report_master(install_salt): """ Test running --versions-report on master """ - if not install_salt.relenv and not install_salt.classic: - pytest.skip("Unable to get the python version dynamically from tiamat builds") test_bin = os.path.join(*install_salt.binary_paths["master"]) python_bin = os.path.join(*install_salt.binary_paths["python"]) ret = install_salt.proc.run(test_bin, "--versions-report") @@ -39,25 +53,26 @@ def test_salt_versions_report_master(install_salt): @pytest.mark.skip_on_windows -def test_salt_versions_report_minion(salt_cli, salt_minion): +def test_salt_versions_report_minion(salt_cli, salt_call_cli, salt_minion): """ Test running test.versions_report on minion """ # Make sure the minion is running assert salt_minion.is_running() + # Make sure we can ping the minion ... ret = salt_cli.run( - "--timeout=240", "test.ping", minion_tgt=salt_minion.id, _timeout=240 + "--timeout=300", "test.ping", minion_tgt=salt_minion.id, _timeout=300 ) assert ret.returncode == 0 assert ret.data is True ret = salt_cli.run( "--hard-crash", "--failhard", - "--timeout=240", + "--timeout=300", "test.versions_report", minion_tgt=salt_minion.id, - _timeout=240, + _timeout=300, ) ret.stdout.matcher.fnmatch_lines(["*Salt Version:*"]) @@ -65,11 +80,15 @@ def test_salt_versions_report_minion(salt_cli, salt_minion): @pytest.mark.parametrize( "binary", ["master", "cloud", "syndic", "minion", "call", "api"] ) -def test_compare_versions(version, binary, install_salt): +def test_compare_versions(binary, install_salt): """ Test compare versions """ + version = install_salt.artifact_version if binary in install_salt.binary_paths: + if install_salt.upgrade: + install_salt.install() + ret = install_salt.proc.run( *install_salt.binary_paths[binary], "--version", @@ -109,15 +128,12 @@ def test_symlinks_created(version, symlink, install_salt): """ Test symlinks created """ - if install_salt.classic: - pytest.skip("Symlinks not created for classic macos builds, we adjust the path") - if not install_salt.relenv and symlink == "spm": - symlink = "salt-spm" ret = install_salt.proc.run(pathlib.Path("/usr/local/sbin") / symlink, "--version") ret.stdout.matcher.fnmatch_lines([f"*{version}*"]) @pytest.mark.skip_on_windows +@pytest.mark.skip_on_darwin @pytest.mark.skip_if_binaries_missing("rpmdev-vercmp") def test_compare_pkg_versions_redhat_rc(version, install_salt): """ diff --git a/tests/pytests/pkg/upgrade/test_salt_upgrade.py b/tests/pytests/pkg/upgrade/test_salt_upgrade.py index fd883705c4a..5bce37d6aeb 100644 --- a/tests/pytests/pkg/upgrade/test_salt_upgrade.py +++ b/tests/pytests/pkg/upgrade/test_salt_upgrade.py @@ -1,38 +1,128 @@ -import logging +import time import packaging.version import psutil +import pytest from pytestskipmarkers.utils import platform -log = logging.getLogger(__name__) +pytestmark = [pytest.mark.skip_unless_on_linux(reason="Only supported on Linux family")] -def _get_running_salt_minion_pid(process_name): - # psutil process name only returning first part of the command '/opt/saltstack/' - # need to check all of command line for salt-minion - # ['/opt/saltstack/salt/bin/python3.10 /usr/bin/salt-minion MultiMinionProcessManager MinionProcessManager'] - # and psutil is only returning the salt-minion once +@pytest.fixture +def salt_systemd_setup( + salt_call_cli, + install_salt, +): + """ + Fixture to set systemd for salt packages to enabled and active + Note: assumes Salt packages already installed + """ + # ensure known state, enabled and active + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + test_cmd = f"systemctl enable {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + assert ret.returncode == 0 + + test_cmd = f"systemctl restart {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + assert ret.returncode == 0 + + +@pytest.fixture +def salt_test_upgrade( + salt_call_cli, + install_salt, +): + """ + Test upgrade of Salt packages for Minion and Master + """ + # Verify previous install version salt-minion is setup correctly and works + ret = salt_call_cli.run("--local", "test.version") + assert ret.returncode == 0 + installed_minion_version = packaging.version.parse(ret.data) + assert installed_minion_version < packaging.version.parse( + install_salt.artifact_version + ) + + # Verify previous install version salt-master is setup correctly and works + bin_file = "salt" + ret = install_salt.proc.run(bin_file, "--version") + assert ret.returncode == 0 + assert packaging.version.parse( + ret.stdout.strip().split()[1] + ) < packaging.version.parse(install_salt.artifact_version) + + # Verify there is a running minion and master by getting there PIDs + process_master_name = "salt-master" + if platform.is_windows(): + process_minion_name = "salt-minion.exe" + else: + process_minion_name = "salt-minion" + + old_minion_pids = _get_running_named_salt_pid(process_minion_name) + old_master_pids = _get_running_named_salt_pid(process_master_name) + assert old_minion_pids + assert old_master_pids + + # Upgrade Salt (inc. minion, master, etc.) from previous version and test + install_salt.install(upgrade=True) + + time.sleep(60) # give it some time + + ret = salt_call_cli.run("--local", "test.version") + assert ret.returncode == 0 + + installed_minion_version = packaging.version.parse(ret.data) + assert installed_minion_version == packaging.version.parse( + install_salt.artifact_version + ) + + ret = install_salt.proc.run(bin_file, "--version") + assert ret.returncode == 0 + assert packaging.version.parse( + ret.stdout.strip().split()[1] + ) == packaging.version.parse(install_salt.artifact_version) + + # Verify there is a new running minion and master by getting their PID and comparing them + # with previous PIDs from before the upgrade + + new_minion_pids = _get_running_named_salt_pid(process_minion_name) + new_master_pids = _get_running_named_salt_pid(process_master_name) + + assert new_minion_pids + assert new_master_pids + assert new_minion_pids != old_minion_pids + assert new_master_pids != old_master_pids + + +def _get_running_named_salt_pid(process_name): + + # need to check all of command line for salt-minion, salt-master, for example: salt-minion + # + # Linux: psutil process name only returning first part of the command '/opt/saltstack/' + # Linux: ['/opt/saltstack/salt/bin/python3.10 /usr/bin/salt-minion MultiMinionProcessManager MinionProcessManager'] + # + # MacOS: psutil process name only returning last part of the command '/opt/salt/bin/python3.10', that is 'python3.10' + # MacOS: ['/opt/salt/bin/python3.10 /opt/salt/salt-minion', ''] + pids = [] for proc in psutil.process_iter(): - if "salt" in proc.name(): - cmdl_strg = " ".join(str(element) for element in proc.cmdline()) - if process_name in cmdl_strg: - pids.append(proc.pid) + cmdl_strg = " ".join(str(element) for element in proc.cmdline()) + if process_name in cmdl_strg: + pids.append(proc.pid) + return pids def test_salt_upgrade(salt_call_cli, install_salt): """ - Test an upgrade of Salt. + Test an upgrade of Salt, Minion and Master """ - if install_salt.relenv: - original_py_version = install_salt.package_python_version() + if not install_salt.upgrade: + pytest.skip("Not testing an upgrade, do not run") - # Verify previous install version is setup correctly and works - ret = salt_call_cli.run("--local", "test.version") - assert ret.returncode == 0 - installed_version = packaging.version.parse(ret.data) - assert installed_version < packaging.version.parse(install_salt.artifact_version) + original_py_version = install_salt.package_python_version() # Test pip install before an upgrade dep = "PyGithub==1.56.0" @@ -44,43 +134,12 @@ def test_salt_upgrade(salt_call_cli, install_salt): use_lib = salt_call_cli.run("--local", "github.get_repo_info", repo) assert "Authentication information could" in use_lib.stderr - # Verify there is a running minion by getting its PID - if installed_version < packaging.version.parse("3006.0"): - # This is using PyInstaller - process_name = "run minion" - else: - if platform.is_windows(): - process_name = "salt-minion.exe" - else: - process_name = "salt-minion" - old_pids = _get_running_salt_minion_pid(process_name) - assert old_pids + # perform Salt package upgrade test + # pylint: disable=pointless-statement + salt_test_upgrade - # Upgrade Salt from previous version and test - install_salt.install(upgrade=True) - ret = salt_call_cli.run("--local", "test.version") - assert ret.returncode == 0 - installed_version = packaging.version.parse(ret.data) - assert installed_version == packaging.version.parse(install_salt.artifact_version) - - # Verify there is a new running minion by getting its PID and comparing it - # with the PID from before the upgrade - if installed_version < packaging.version.parse("3006.0"): - # This is using PyInstaller - process_name = "run minion" - else: - if platform.is_windows(): - process_name = "salt-minion.exe" - else: - process_name = "salt-minion" - new_pids = _get_running_salt_minion_pid(process_name) - - assert new_pids - assert new_pids != old_pids - - if install_salt.relenv: - new_py_version = install_salt.package_python_version() - if new_py_version == original_py_version: - # test pip install after an upgrade - use_lib = salt_call_cli.run("--local", "github.get_repo_info", repo) - assert "Authentication information could" in use_lib.stderr + new_py_version = install_salt.package_python_version() + if new_py_version == original_py_version: + # test pip install after an upgrade + use_lib = salt_call_cli.run("--local", "github.get_repo_info", repo) + assert "Authentication information could" in use_lib.stderr diff --git a/tests/pytests/pkg/upgrade/test_systemd_permissions.py b/tests/pytests/pkg/upgrade/test_systemd_permissions.py new file mode 100644 index 00000000000..12afeb5f656 --- /dev/null +++ b/tests/pytests/pkg/upgrade/test_systemd_permissions.py @@ -0,0 +1,343 @@ +import time + +import pytest + +pytestmark = [ + pytest.mark.skip_unless_on_linux(reason="Only supported on Linux family"), + pytest.mark.skipif( + True, + reason=( + "Package permissions are getting reworked in " + "https://github.com/saltstack/salt/pull/66218" + ), + ), +] + + +@pytest.fixture +def salt_systemd_setup( + salt_call_cli, + install_salt, +): + """ + Fixture to set systemd for salt packages to enabled and active + Note: assumes Salt packages already installed + """ + install_salt.install() + + # ensure known state, enabled and active + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + test_cmd = f"systemctl enable {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + assert ret.returncode == 0 + + test_cmd = f"systemctl restart {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + assert ret.returncode == 0 + + +def test_salt_systemd_disabled_preservation( + salt_call_cli, install_salt, salt_systemd_setup +): + """ + Test upgrade of Salt packages preserve disabled state of systemd + """ + if not install_salt.upgrade: + pytest.skip("Not testing an upgrade, do not run") + + # setup systemd to enabled and active for Salt packages + # pylint: disable=pointless-statement + salt_systemd_setup + + # ensure known state, disabled + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + test_cmd = f"systemctl disable {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + assert ret.returncode == 0 + + # Upgrade Salt (inc. minion, master, etc.) from previous version and test + # pylint: disable=pointless-statement + install_salt.install(upgrade=True) + time.sleep(60) # give it some time + + # test for disabled systemd state + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + test_cmd = f"systemctl show -p UnitFileState {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_enabled = ret.stdout.strip().split("=")[1].split('"')[0].strip() + assert ret.returncode == 0 + assert test_enabled == "disabled" + + +def test_salt_systemd_enabled_preservation( + salt_call_cli, install_salt, salt_systemd_setup +): + """ + Test upgrade of Salt packages preserve enabled state of systemd + """ + if not install_salt.upgrade: + pytest.skip("Not testing an upgrade, do not run") + + # setup systemd to enabled and active for Salt packages + # pylint: disable=pointless-statement + salt_systemd_setup + + # Upgrade Salt (inc. minion, master, etc.) from previous version and test + # pylint: disable=pointless-statement + install_salt.install(upgrade=True) + time.sleep(60) # give it some time + + # test for enabled systemd state + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + test_cmd = f"systemctl show -p UnitFileState {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_enabled = ret.stdout.strip().split("=")[1].split('"')[0].strip() + assert ret.returncode == 0 + assert test_enabled == "enabled" + + +def test_salt_systemd_inactive_preservation( + salt_call_cli, install_salt, salt_systemd_setup +): + """ + Test upgrade of Salt packages preserve inactive state of systemd + """ + if not install_salt.upgrade: + pytest.skip("Not testing an upgrade, do not run") + + # setup systemd to enabled and active for Salt packages + # pylint: disable=pointless-statement + salt_systemd_setup + + # ensure known state, disabled + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + test_cmd = f"systemctl stop {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + assert ret.returncode == 0 + + # Upgrade Salt (inc. minion, master, etc.) from previous version and test + # pylint: disable=pointless-statement + install_salt.install(upgrade=True) + time.sleep(60) # give it some time + + # test for inactive systemd state + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + test_cmd = f"systemctl is-active {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_active = ret.stdout.strip().split()[2].strip('"').strip() + assert ret.returncode == 1 + assert test_active == "inactive" + + +def test_salt_systemd_active_preservation( + salt_call_cli, install_salt, salt_systemd_setup +): + """ + Test upgrade of Salt packages preserve active state of systemd + """ + if not install_salt.upgrade: + pytest.skip("Not testing an upgrade, do not run") + + # setup systemd to enabled and active for Salt packages + # pylint: disable=pointless-statement + salt_systemd_setup + + # Upgrade Salt (inc. minion, master, etc.) from previous version and test + # pylint: disable=pointless-statement + install_salt.install(upgrade=True) + time.sleep(60) # give it some time + + # test for active systemd state + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + test_cmd = f"systemctl is-active {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_active = ret.stdout.strip().split()[2].strip('"').strip() + assert ret.returncode == 0 + assert test_active == "active" + + +def test_salt_ownership_permission(salt_call_cli, install_salt, salt_systemd_setup): + """ + Test upgrade of Salt packages preserve existing ownership + """ + if not install_salt.upgrade: + pytest.skip("Not testing an upgrade, do not run") + + # setup systemd to enabled and active for Salt packages + # pylint: disable=pointless-statement + salt_systemd_setup + + # test ownership for Minion, Master and Api + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + if "salt-api" == test_item: + test_cmd = f"ls -dl /run/{test_item}.pid" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_user = ret.stdout.strip().split()[4] + assert ret.returncode == 0 + assert test_user == "salt" + + test_cmd = f"ls -dl /run/{test_item}.pid" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_group = ret.stdout.strip().split()[5] + assert ret.returncode == 0 + assert test_group == "salt" + else: + test_name = test_item.strip().split("-")[1] + test_cmd = f"ls -dl /run/salt/{test_name}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_user = ret.stdout.strip().split()[4] + assert ret.returncode == 0 + if test_item == "salt-minion": + assert test_user == "root" + else: + assert test_user == "salt" + + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_group = ret.stdout.strip().split()[5] + assert ret.returncode == 0 + if test_item == "salt-minion": + assert test_group == "root" + else: + assert test_group == "salt" + + # create master user, and minion user, change conf, restart and test ownership + test_master_user = "horse" + test_minion_user = "donkey" + ret = salt_call_cli.run("--local", "user.list_users") + user_list = ret.stdout.strip().split(":")[1] + + if test_master_user not in user_list: + ret = salt_call_cli.run("--local", "user.add", f"{test_master_user}") + + if test_minion_user not in user_list: + ret = salt_call_cli.run("--local", "user.add", f"{test_minion_user}") + + ret = salt_call_cli.run( + "--local", "file.comment_line", "/etc/salt/master", "^user:" + ) + assert ret.returncode == 0 + + ret = salt_call_cli.run( + "--local", "file.comment_line", "/etc/salt/minion", "^user:" + ) + assert ret.returncode == 0 + + test_string = f"\nuser: {test_master_user}\n" + ret = salt_call_cli.run("--local", "file.append", "/etc/salt/master", test_string) + + test_string = f"\nuser: {test_minion_user}\n" + ret = salt_call_cli.run("--local", "file.append", "/etc/salt/minion", test_string) + + # restart and check ownership is correct + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + test_cmd = f"systemctl restart {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + + time.sleep(10) # allow some time for restart + + # test ownership for Minion, Master and Api - horse and donkey + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + if "salt-api" == test_item: + test_cmd = f"ls -dl /run/{test_item}.pid" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_user = ret.stdout.strip().split()[4] + assert ret.returncode == 0 + assert test_user == f"{test_master_user}" + + test_cmd = f"ls -dl /run/{test_item}.pid" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_group = ret.stdout.strip().split()[5] + assert ret.returncode == 0 + assert test_group == f"{test_master_user}" + else: + test_name = test_item.strip().split("-")[1] + test_cmd = f"ls -dl /run/salt/{test_name}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_user = ret.stdout.strip().split()[4] + assert ret.returncode == 0 + if test_item == "salt-minion": + assert test_user == f"{test_minion_user}" + else: + assert test_user == f"{test_master_user}" + + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_group = ret.stdout.strip().split()[5] + assert ret.returncode == 0 + if test_item == "salt-minion": + assert test_group == f"{test_minion_user}" + else: + assert test_group == f"{test_master_user}" + + # Upgrade Salt (inc. minion, master, etc.) from previous version and test + # pylint: disable=pointless-statement + install_salt.install(upgrade=True) + time.sleep(60) # give it some time + + # test ownership for Minion, Master and Api + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + if "salt-api" == test_item: + test_cmd = f"ls -dl /run/{test_item}.pid" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_user = ret.stdout.strip().split()[4] + assert ret.returncode == 0 + assert test_user == f"{test_master_user}" + + test_cmd = f"ls -dl /run/{test_item}.pid" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_group = ret.stdout.strip().split()[5] + assert ret.returncode == 0 + assert test_group == f"{test_master_user}" + else: + test_name = test_item.strip().split("-")[1] + test_cmd = f"ls -dl /run/salt/{test_name}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_user = ret.stdout.strip().split()[4] + assert ret.returncode == 0 + if test_item == "salt-minion": + assert test_user == f"{test_minion_user}" + else: + assert test_user == f"{test_master_user}" + + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + test_group = ret.stdout.strip().split()[5] + assert ret.returncode == 0 + if test_item == "salt-minion": + assert test_group == f"{test_minion_user}" + else: + assert test_group == f"{test_master_user}" + + # restore to defaults to ensure further tests run fine + ret = salt_call_cli.run( + "--local", "file.comment_line", "/etc/salt/master", "^user:" + ) + assert ret.returncode == 0 + + ret = salt_call_cli.run( + "--local", "file.comment_line", "/etc/salt/minion", "^user:" + ) + assert ret.returncode == 0 + + test_string = "\nuser: salt\n" + ret = salt_call_cli.run("--local", "file.append", "/etc/salt/master", test_string) + + test_string = "\nuser: root\n" + ret = salt_call_cli.run("--local", "file.append", "/etc/salt/minion", test_string) + + # restart and check ownership is correct + test_list = ["salt-api", "salt-minion", "salt-master"] + for test_item in test_list: + test_cmd = f"systemctl restart {test_item}" + ret = salt_call_cli.run("--local", "cmd.run", test_cmd) + + time.sleep(10) # allow some time for restart diff --git a/tests/pytests/unit/channel/test_client.py b/tests/pytests/unit/channel/test_client.py new file mode 100644 index 00000000000..783657c4a45 --- /dev/null +++ b/tests/pytests/unit/channel/test_client.py @@ -0,0 +1,19 @@ +import salt.channel.client + + +def test_async_methods(): + "Validate all defined async_methods and close_methods are present" + async_classes = [ + salt.channel.client.AsyncReqChannel, + salt.channel.client.AsyncPubChannel, + ] + method_attrs = [ + "async_methods", + "close_methods", + ] + for cls in async_classes: + for attr in method_attrs: + assert hasattr(cls, attr) + assert isinstance(getattr(cls, attr), list) + for name in getattr(cls, attr): + assert hasattr(cls, name) diff --git a/tests/pytests/unit/fileserver/test_roots.py b/tests/pytests/unit/fileserver/test_roots.py index a197b937eec..124c491ce15 100644 --- a/tests/pytests/unit/fileserver/test_roots.py +++ b/tests/pytests/unit/fileserver/test_roots.py @@ -341,3 +341,13 @@ def test_serve_file_symlink_destination_not_in_root(tmp_state_tree): fnd = {"path": str(symlink / "testfile"), "rel": "bar/testfile"} ret = roots.serve_file(load, fnd) assert ret == {"data": b"testfile", "dest": "bar/testfile"} + + +def test_relative_file_roots(tmp_state_tree): + parent = pathlib.Path(tmp_state_tree).parent + reldir = os.path.basename(tmp_state_tree) + opts = {"file_roots": copy.copy(roots.__opts__["file_roots"])} + opts["file_roots"]["base"] = [reldir] + with patch.dict(roots.__opts__, opts), pytest.helpers.change_cwd(str(parent)): + ret = roots.find_file("testfile") + assert "testfile" == ret["rel"] diff --git a/tests/pytests/unit/grains/test_core.py b/tests/pytests/unit/grains/test_core.py index fc45f0c3d91..0dfea8eb813 100644 --- a/tests/pytests/unit/grains/test_core.py +++ b/tests/pytests/unit/grains/test_core.py @@ -1231,6 +1231,39 @@ def test_Parrot_OS_grains(): _run_os_grains_tests(_os_release_data, _os_release_map, expectation) +@pytest.mark.skip_unless_on_linux +def test_manjaro_arm_grains(): + """ + Test if OS grains are parsed correctly in Manjaro ARM + """ + # /etc/os-release data taken from ParrotOS 5.1 + _os_release_data = { + "NAME": "Manjaro ARM", + "ID": "manjaro-arm", + "ID_LIKE": "manjaro arch", + "PRETTY_NAME": "Manjaro ARM", + "ANSI_COLOR": "1;32", + "HOME_URL": "https://www.manjaro.org/", + "SUPPORT_URL": "https://forum.manjaro.org/c/arm/", + "LOGO": "manjarolinux", + } + _os_release_map = { + "_linux_distribution": ("Manjaro ARM", "24.03", "n/a"), + } + + expectation = { + "os": "Manjaro ARM", + "os_family": "Arch", + "oscodename": "Manjaro ARM", + "osfullname": "Manjaro ARM", + "osrelease": "24.03", + "osrelease_info": (24, 3), + "osmajorrelease": 24, + "osfinger": "Manjaro ARM-24", + } + _run_os_grains_tests(_os_release_data, _os_release_map, expectation) + + def test_unicode_error(): raise_unicode_mock = MagicMock(name="raise_unicode_error", side_effect=UnicodeError) with patch("salt.grains.core.hostname"), patch( diff --git a/tests/pytests/unit/modules/file/test_file_block_replace.py b/tests/pytests/unit/modules/file/test_file_block_replace.py index 8a05154f41c..8cc9b818b51 100644 --- a/tests/pytests/unit/modules/file/test_file_block_replace.py +++ b/tests/pytests/unit/modules/file/test_file_block_replace.py @@ -48,6 +48,7 @@ def configure_loader_modules(): "__grains__": grains, "__utils__": { "files.is_binary": MagicMock(return_value=False), + "files.is_text": salt.utils.files.is_text, "files.get_encoding": MagicMock(return_value="utf-8"), "stringutils.get_diff": salt.utils.stringutils.get_diff, }, @@ -546,3 +547,17 @@ def test_unfinished_block_exception(multiline_file): content="foobar", backup=False, ) + + +def test_search_proc_file(): + """ + Test that searching content in a /proc file does not raise a TypeError + and handles bytes correctly. + """ + proc_file_path = "/proc/cpuinfo" + + if not os.path.exists(proc_file_path): + pytest.skip(f"{proc_file_path} not available") + + match_found = filemod.search(proc_file_path, "processor") + assert match_found, "Failed to find 'processor' in /proc/cpuinfo" diff --git a/tests/pytests/unit/modules/test_cmdmod.py b/tests/pytests/unit/modules/test_cmdmod.py index cfc031fc063..e1f2a604cd1 100644 --- a/tests/pytests/unit/modules/test_cmdmod.py +++ b/tests/pytests/unit/modules/test_cmdmod.py @@ -1059,7 +1059,14 @@ def test_prep_powershell_cmd_no_powershell(): ) -def test_prep_powershell_cmd(): +@pytest.mark.parametrize( + "cmd, parsed", + [ + ("Write-Host foo", "& Write-Host foo"), + ("$PSVersionTable", "$PSVersionTable"), + ], +) +def test_prep_powershell_cmd(cmd, parsed): """ Tests _prep_powershell_cmd returns correct cmd """ @@ -1068,7 +1075,7 @@ def test_prep_powershell_cmd(): "salt.utils.path.which", return_value="C:\\powershell.exe" ): ret = cmdmod._prep_powershell_cmd( - win_shell="powershell", cmd="$PSVersionTable", encoded_cmd=False + win_shell="powershell", cmd=cmd, encoded_cmd=False ) expected = [ "C:\\powershell.exe", @@ -1077,7 +1084,7 @@ def test_prep_powershell_cmd(): "-ExecutionPolicy", "Bypass", "-Command", - "& {$PSVersionTable}", + parsed, ] assert ret == expected diff --git a/tests/pytests/unit/modules/test_ini_manage.py b/tests/pytests/unit/modules/test_ini_manage.py index 499bae71e06..e226f34dfaa 100644 --- a/tests/pytests/unit/modules/test_ini_manage.py +++ b/tests/pytests/unit/modules/test_ini_manage.py @@ -94,24 +94,22 @@ def test_get_option(encoding, linesep, ini_file, ini_content): ) ini_file.write_bytes(content) - assert ( - ini.get_option(str(ini_file), "main", "test1", encoding=encoding) == "value 1" - ) - assert ( - ini.get_option(str(ini_file), "main", "test2", encoding=encoding) == "value 2" - ) - assert ( - ini.get_option(str(ini_file), "SectionB", "test1", encoding=encoding) - == "value 1B" - ) - assert ( - ini.get_option(str(ini_file), "SectionB", "test3", encoding=encoding) - == "value 3B" - ) - assert ( - ini.get_option(str(ini_file), "SectionC", "empty_option", encoding=encoding) - == "" + option = ini.get_option(str(ini_file), "main", "test1", encoding=encoding) + assert option == "value 1" + + option = ini.get_option(str(ini_file), "main", "test2", encoding=encoding) + assert option == "value 2" + + option = ini.get_option(str(ini_file), "SectionB", "test1", encoding=encoding) + assert option == "value 1B" + + option = ini.get_option(str(ini_file), "SectionB", "test3", encoding=encoding) + assert option == "value 3B" + + option = ini.get_option( + str(ini_file), "SectionC", "empty_option", encoding=encoding ) + assert option == "" @pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"]) @@ -249,11 +247,12 @@ def test_set_option(encoding, linesep, ini_file, ini_content): ) +@pytest.mark.parametrize("no_spaces", [True, False]) @pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"]) @pytest.mark.parametrize( "encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"] ) -def test_empty_value(encoding, linesep, ini_file, ini_content): +def test_empty_value(encoding, linesep, no_spaces, ini_file, ini_content): """ Test empty value preserved after edit """ @@ -263,19 +262,23 @@ def test_empty_value(encoding, linesep, ini_file, ini_content): ini_file.write_bytes(content) ini.set_option( - str(ini_file), {"SectionB": {"test3": "new value 3B"}}, encoding=encoding + str(ini_file), + {"SectionB": {"test3": "new value 3B"}}, + encoding=encoding, + no_spaces=no_spaces, ) with salt.utils.files.fopen(str(ini_file), "r") as fp_: file_content = salt.utils.stringutils.to_unicode(fp_.read(), encoding=encoding) - expected = "{0}{1}{0}".format(os.linesep, "empty_option = ") + expected = f"{os.linesep}empty_option{'=' if no_spaces else ' = '}{os.linesep}" assert expected in file_content, "empty_option was not preserved" +@pytest.mark.parametrize("no_spaces", [True, False]) @pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"]) @pytest.mark.parametrize( "encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"] ) -def test_empty_lines(encoding, linesep, ini_file, ini_content): +def test_empty_lines(encoding, linesep, no_spaces, ini_file, ini_content): """ Test empty lines preserved after edit """ @@ -289,42 +292,48 @@ def test_empty_lines(encoding, linesep, ini_file, ini_content): "# Comment on the first line", "", "# First main option", - "option1 = main1", + f"option1{'=' if no_spaces else ' = '}main1", "", "# Second main option", - "option2 = main2", + f"option2{'=' if no_spaces else ' = '}main2", "", "[main]", "# Another comment", - "test1 = value 1", + f"test1{'=' if no_spaces else ' = '}value 1", "", - "test2 = value 2", + f"test2{'=' if no_spaces else ' = '}value 2", "", "[SectionB]", - "test1 = value 1B", + f"test1{'=' if no_spaces else ' = '}value 1B", "", "# Blank line should be above", - "test3 = new value 3B", + f"test3{'=' if no_spaces else ' = '}new value 3B", "", "[SectionC]", "# The following option is empty", - "empty_option = ", + f"empty_option{'=' if no_spaces else ' = '}", "", ] ) ini.set_option( - str(ini_file), {"SectionB": {"test3": "new value 3B"}}, encoding=encoding + str(ini_file), + {"SectionB": {"test3": "new value 3B"}}, + encoding=encoding, + no_spaces=no_spaces, ) with salt.utils.files.fopen(str(ini_file), "r") as fp_: file_content = fp_.read() assert expected == file_content +@pytest.mark.parametrize("no_spaces", [True, False]) @pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"]) @pytest.mark.parametrize( "encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"] ) -def test_empty_lines_multiple_edits(encoding, linesep, ini_file, ini_content): +def test_empty_lines_multiple_edits( + encoding, linesep, no_spaces, ini_file, ini_content +): """ Test empty lines preserved after multiple edits """ @@ -337,6 +346,7 @@ def test_empty_lines_multiple_edits(encoding, linesep, ini_file, ini_content): str(ini_file), {"SectionB": {"test3": "this value will be edited two times"}}, encoding=encoding, + no_spaces=no_spaces, ) expected = os.linesep.join( @@ -344,31 +354,34 @@ def test_empty_lines_multiple_edits(encoding, linesep, ini_file, ini_content): "# Comment on the first line", "", "# First main option", - "option1 = main1", + f"option1{'=' if no_spaces else ' = '}main1", "", "# Second main option", - "option2 = main2", + f"option2{'=' if no_spaces else ' = '}main2", "", "[main]", "# Another comment", - "test1 = value 1", + f"test1{'=' if no_spaces else ' = '}value 1", "", - "test2 = value 2", + f"test2{'=' if no_spaces else ' = '}value 2", "", "[SectionB]", - "test1 = value 1B", + f"test1{'=' if no_spaces else ' = '}value 1B", "", "# Blank line should be above", - "test3 = new value 3B", + f"test3{'=' if no_spaces else ' = '}new value 3B", "", "[SectionC]", "# The following option is empty", - "empty_option = ", + f"empty_option{'=' if no_spaces else ' = '}", "", ] ) ini.set_option( - str(ini_file), {"SectionB": {"test3": "new value 3B"}}, encoding=encoding + str(ini_file), + {"SectionB": {"test3": "new value 3B"}}, + encoding=encoding, + no_spaces=no_spaces, ) with salt.utils.files.fopen(str(ini_file), "r") as fp_: file_content = fp_.read() diff --git a/tests/pytests/unit/modules/test_pip.py b/tests/pytests/unit/modules/test_pip.py index 0684c3fdd05..b2b5a8988d0 100644 --- a/tests/pytests/unit/modules/test_pip.py +++ b/tests/pytests/unit/modules/test_pip.py @@ -474,10 +474,10 @@ def test_install_venv(): ) -def test_install_log_argument_in_resulting_command(python_binary): +def test_install_log_argument_in_resulting_command(python_binary, tmp_path): with patch("os.access") as mock_path: pkg = "pep8" - log_path = "/tmp/pip-install.log" + log_path = str(tmp_path / "pip-install.log") mock = MagicMock(return_value={"retcode": 0, "stdout": ""}) with patch.dict(pip.__salt__, {"cmd.run_all": mock}): pip.install(pkg, log=log_path) diff --git a/tests/pytests/unit/modules/test_win_status.py b/tests/pytests/unit/modules/test_win_status.py index c941b9ccfa1..236e164935b 100644 --- a/tests/pytests/unit/modules/test_win_status.py +++ b/tests/pytests/unit/modules/test_win_status.py @@ -16,9 +16,11 @@ def test__get_connected_ips(): conns = psutil.net_connections() for conn in conns: if conn.status == psutil.CONN_ESTABLISHED: - ip = conn.laddr.ip - port = conn.laddr.port + ip = conn.raddr.ip + port = conn.raddr.port break assert port is not None assert ip is not None - assert win_status._get_connected_ips(port) == {ip} + # Since this may return more than one IP, let's make sure our test IP is in + # the list of IPs + assert ip in win_status._get_connected_ips(port) diff --git a/tests/pytests/unit/output/test_profile.py b/tests/pytests/unit/output/test_profile.py new file mode 100644 index 00000000000..8a6234c1821 --- /dev/null +++ b/tests/pytests/unit/output/test_profile.py @@ -0,0 +1,94 @@ +import pytest + +import salt.output.profile as profile + + +@pytest.fixture +def configure_loader_modules(): + return {profile: {"__opts__": {"extension_modules": "", "color": False}}} + + +def test_no_states_found(): + """ + Simulate the result of the "profile" outputter with state.apply. + i.e. salt-call --local state.apply --output=profile + """ + data = { + "local": { + "no_|-states_|-states_|-None": { + "result": False, + "comment": "No Top file or master_tops data matches found. Please see master log for details.", + "name": "No States", + "changes": {}, + "__run_num__": 0, + } + } + } + + expected_output = ( + " ---------------------------------------\n" + " | name | mod.fun | duration (ms) |\n" + " ---------------------------------------\n" + " | No States | no.None | -1.0000 |\n" + " ---------------------------------------" + ) + + ret = profile.output(data) + assert expected_output in ret + + +def test_no_matching_sls(): + """ + Simulate the result of the "profile" outputter with state.sls. + i.e. salt-call --local state.sls foo --output=profile + """ + data = {"local": ["No matching sls found for 'foo' in env 'base'"]} + + expected_output = ( + " ---------------------------------------------------------------------------\n" + " | name | mod.fun | duration (ms) |\n" + " ---------------------------------------------------------------------------\n" + " | <> | No matching sls found for 'foo' in env 'base'.No | -1.0000 |\n" + " ---------------------------------------------------------------------------" + ) + + ret = profile.output(data) + assert expected_output in ret + + +def test_output_with_grains_data(): + """ + Simulate the result of the "profile" outputter with grains data. + i.e. salt-call --local grains.items --output=profile + """ + grains_data = { + "local": { + "dns": {"nameservers": ["0.0.0.0", "1.1.1.1"], "search": ["dns.com"]}, + "fqdns": [], + "disks": ["sda"], + "ssds": ["nvme0n1"], + "shell": "/bin/bash", + "efi-secure-boot": False, + } + } + + ret = profile.output(grains_data) + expected_ret = ( + " ---------------------------------------------------------------------\n" + " | name | mod.fun | duration (ms) |\n" + " ---------------------------------------------------------------------\n" + " | <> | dns.dns | -1.0000 |\n" + " ---------------------------------------------------------------------\n" + " | disks | disks.disks | -1.0000 |\n" + " ---------------------------------------------------------------------\n" + " | efi-secure-boot | efi-secure-boot.efi-secure-boot | -1.0000 |\n" + " ---------------------------------------------------------------------\n" + " | fqdns | fqdns.fqdns | -1.0000 |\n" + " ---------------------------------------------------------------------\n" + " | shell | shell.shell | -1.0000 |\n" + " ---------------------------------------------------------------------\n" + " | ssds | ssds.ssds | -1.0000 |\n" + " ---------------------------------------------------------------------" + ) + + assert ret == expected_ret diff --git a/tests/pytests/unit/test_pillar.py b/tests/pytests/unit/test_pillar.py index d44a337981f..1b29c26248d 100644 --- a/tests/pytests/unit/test_pillar.py +++ b/tests/pytests/unit/test_pillar.py @@ -1259,3 +1259,43 @@ def test_compile_pillar_disk_cache(master_opts, grains): "mocked_minion": {"base": {"foo": "bar"}, "dev": {"foo": "baz"}} } assert pillar.cache._dict == expected_cache + + +def test_remote_pillar_bad_return(grains, tmp_pki): + opts = { + "pki_dir": tmp_pki, + "id": "minion", + "master_uri": "tcp://127.0.0.1:4505", + "__role": "minion", + "keysize": 2048, + "saltenv": "base", + "pillarenv": "base", + } + pillar = salt.pillar.RemotePillar(opts, grains, "mocked-minion", "dev") + + async def crypted_transfer_mock(): + return "" + + pillar.channel.crypted_transfer_decode_dictentry = crypted_transfer_mock + with pytest.raises(salt.exceptions.SaltClientError): + pillar.compile_pillar() + + +async def test_async_remote_pillar_bad_return(grains, tmp_pki): + opts = { + "pki_dir": tmp_pki, + "id": "minion", + "master_uri": "tcp://127.0.0.1:4505", + "__role": "minion", + "keysize": 2048, + "saltenv": "base", + "pillarenv": "base", + } + pillar = salt.pillar.AsyncRemotePillar(opts, grains, "mocked-minion", "dev") + + async def crypted_transfer_mock(): + return "" + + pillar.channel.crypted_transfer_decode_dictentry = crypted_transfer_mock + with pytest.raises(salt.exceptions.SaltClientError): + await pillar.compile_pillar() diff --git a/tests/pytests/unit/transport/test_zeromq.py b/tests/pytests/unit/transport/test_zeromq.py index 609b70ab48a..2c907bebac5 100644 --- a/tests/pytests/unit/transport/test_zeromq.py +++ b/tests/pytests/unit/transport/test_zeromq.py @@ -1,8 +1,11 @@ import ctypes +import hashlib import logging import multiprocessing +import os import threading import time +import uuid import msgpack import pytest @@ -17,7 +20,7 @@ import salt.utils.process import salt.utils.stringutils from salt.master import SMaster from tests.conftest import FIPS_TESTRUN -from tests.support.mock import AsyncMock, MagicMock +from tests.support.mock import AsyncMock, MagicMock, patch log = logging.getLogger(__name__) @@ -354,6 +357,1085 @@ class MockSaltMinionMaster: raise tornado.gen.Return((payload, {"fun": "send_clear"})) +@pytest.mark.parametrize("message", ["", [], ()]) +def test_badload(temp_salt_minion, temp_salt_master, message): + """ + Test a variety of bad requests, make sure that we get some sort of error + """ + with MockSaltMinionMaster(temp_salt_minion, temp_salt_master) as minion_master: + ret = minion_master.channel.send(message, timeout=5, tries=1) + assert ret == "payload and load must be a dict" + + +def test_payload_handling_exception(temp_salt_minion, temp_salt_master): + """ + test of getting exception on payload handling + """ + with MockSaltMinionMaster(temp_salt_minion, temp_salt_master) as minion_master: + with patch.object(minion_master.mock, "_handle_payload_hook") as _mock: + _mock.side_effect = Exception() + ret = minion_master.channel.send({}, timeout=5, tries=1) + assert ret == "Some exception handling minion payload" + + +def test_serverside_exception(temp_salt_minion, temp_salt_master): + """ + test of getting server side exception on payload handling + """ + with MockSaltMinionMaster(temp_salt_minion, temp_salt_master) as minion_master: + with patch.object(minion_master.mock, "_handle_payload_hook") as _mock: + _mock.side_effect = salt.ext.tornado.gen.Return(({}, {"fun": "madeup-fun"})) + ret = minion_master.channel.send({}, timeout=5, tries=1) + assert ret == "Server-side exception handling payload" + + +def test_zeromq_async_pub_channel_publish_port(temp_salt_master): + """ + test when connecting that we use the publish_port set in opts when its not 4506 + """ + opts = dict( + temp_salt_master.config.copy(), + ipc_mode="ipc", + pub_hwm=0, + recon_randomize=False, + publish_port=455505, + recon_default=1, + recon_max=2, + master_ip="127.0.0.1", + acceptance_wait_time=5, + acceptance_wait_time_max=5, + sign_pub_messages=False, + ) + opts["master_uri"] = "tcp://{interface}:{publish_port}".format(**opts) + ioloop = salt.ext.tornado.ioloop.IOLoop() + transport = salt.transport.zeromq.PublishClient(opts, ioloop) + with transport: + patch_socket = MagicMock(return_value=True) + patch_auth = MagicMock(return_value=True) + with patch.object(transport, "_socket", patch_socket): + transport.connect(455505) + assert str(opts["publish_port"]) in patch_socket.mock_calls[0][1][0] + + +def test_zeromq_async_pub_channel_filtering_decode_message_no_match( + temp_salt_master, +): + """ + test zeromq PublishClient _decode_messages when + zmq_filtering enabled and minion does not match + """ + message = [ + b"4f26aeafdb2367620a393c973eddbe8f8b846eb", + b"\x82\xa3enc\xa3aes\xa4load\xda\x00`\xeeR\xcf" + b"\x0eaI#V\x17if\xcf\xae\x05\xa7\xb3bN\xf7\xb2\xe2" + b'\xd0sF\xd1\xd4\xecB\xe8\xaf"/*ml\x80Q3\xdb\xaexg' + b"\x8e\x8a\x8c\xd3l\x03\\,J\xa7\x01i\xd1:]\xe3\x8d" + b"\xf4\x03\x88K\x84\n`\xe8\x9a\xad\xad\xc6\x8ea\x15>" + b"\x92m\x9e\xc7aM\x11?\x18;\xbd\x04c\x07\x85\x99\xa3\xea[\x00D", + ] + + opts = dict( + temp_salt_master.config.copy(), + ipc_mode="ipc", + pub_hwm=0, + zmq_filtering=True, + recon_randomize=False, + recon_default=1, + recon_max=2, + master_ip="127.0.0.1", + acceptance_wait_time=5, + acceptance_wait_time_max=5, + sign_pub_messages=False, + ) + opts["master_uri"] = "tcp://{interface}:{publish_port}".format(**opts) + + ioloop = salt.ext.tornado.ioloop.IOLoop() + channel = salt.transport.zeromq.PublishClient(opts, ioloop) + with channel: + with patch( + "salt.crypt.AsyncAuth.crypticle", + MagicMock(return_value={"tgt_type": "glob", "tgt": "*", "jid": 1}), + ): + res = channel._decode_messages(message) + assert res.result() is None + + +def test_zeromq_async_pub_channel_filtering_decode_message( + temp_salt_master, temp_salt_minion +): + """ + test AsyncZeroMQPublishClient _decode_messages when zmq_filtered enabled + """ + minion_hexid = salt.utils.stringutils.to_bytes( + hashlib.sha1(salt.utils.stringutils.to_bytes(temp_salt_minion.id)).hexdigest() + ) + + message = [ + minion_hexid, + b"\x82\xa3enc\xa3aes\xa4load\xda\x00`\xeeR\xcf" + b"\x0eaI#V\x17if\xcf\xae\x05\xa7\xb3bN\xf7\xb2\xe2" + b'\xd0sF\xd1\xd4\xecB\xe8\xaf"/*ml\x80Q3\xdb\xaexg' + b"\x8e\x8a\x8c\xd3l\x03\\,J\xa7\x01i\xd1:]\xe3\x8d" + b"\xf4\x03\x88K\x84\n`\xe8\x9a\xad\xad\xc6\x8ea\x15>" + b"\x92m\x9e\xc7aM\x11?\x18;\xbd\x04c\x07\x85\x99\xa3\xea[\x00D", + ] + + opts = dict( + temp_salt_master.config.copy(), + id=temp_salt_minion.id, + ipc_mode="ipc", + pub_hwm=0, + zmq_filtering=True, + recon_randomize=False, + recon_default=1, + recon_max=2, + master_ip="127.0.0.1", + acceptance_wait_time=5, + acceptance_wait_time_max=5, + sign_pub_messages=False, + ) + opts["master_uri"] = "tcp://{interface}:{publish_port}".format(**opts) + + ioloop = salt.ext.tornado.ioloop.IOLoop() + channel = salt.transport.zeromq.PublishClient(opts, ioloop) + with channel: + with patch( + "salt.crypt.AsyncAuth.crypticle", + MagicMock(return_value={"tgt_type": "glob", "tgt": "*", "jid": 1}), + ) as mock_test: + res = channel._decode_messages(message) + + assert res.result()["enc"] == "aes" + + +def test_req_server_chan_encrypt_v2( + pki_dir, encryption_algorithm, signing_algorithm, master_opts +): + loop = salt.ext.tornado.ioloop.IOLoop.current() + master_opts.update( + { + "worker_threads": 1, + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "zmq_monitor": False, + "mworker_queue_niceness": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("master")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + } + ) + server = salt.channel.server.ReqServerChannel.factory(master_opts) + dictkey = "pillar" + nonce = "abcdefg" + pillar_data = {"pillar1": "meh"} + ret = server._encrypt_private( + pillar_data, + dictkey, + "minion", + nonce, + encryption_algorithm=encryption_algorithm, + signing_algorithm=signing_algorithm, + ) + assert "key" in ret + assert dictkey in ret + + key = salt.crypt.PrivateKey(str(pki_dir.joinpath("minion", "minion.pem"))) + aes = key.decrypt(ret["key"], encryption_algorithm) + pcrypt = salt.crypt.Crypticle(master_opts, aes) + signed_msg = pcrypt.loads(ret[dictkey]) + + assert "sig" in signed_msg + assert "data" in signed_msg + data = salt.payload.loads(signed_msg["data"]) + assert "key" in data + assert data["key"] == ret["key"] + assert "key" in data + assert data["nonce"] == nonce + assert "pillar" in data + assert data["pillar"] == pillar_data + + +def test_req_server_chan_encrypt_v1(pki_dir, encryption_algorithm, master_opts): + loop = salt.ext.tornado.ioloop.IOLoop.current() + master_opts.update( + { + "worker_threads": 1, + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "zmq_monitor": False, + "mworker_queue_niceness": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("master")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + } + ) + server = salt.channel.server.ReqServerChannel.factory(master_opts) + dictkey = "pillar" + nonce = "abcdefg" + pillar_data = {"pillar1": "meh"} + ret = server._encrypt_private( + pillar_data, + dictkey, + "minion", + sign_messages=False, + encryption_algorithm=encryption_algorithm, + ) + + assert "key" in ret + assert dictkey in ret + + key = salt.crypt.PrivateKey(str(pki_dir.joinpath("minion", "minion.pem"))) + aes = key.decrypt(ret["key"], encryption_algorithm) + pcrypt = salt.crypt.Crypticle(master_opts, aes) + data = pcrypt.loads(ret[dictkey]) + assert data == pillar_data + + +def test_req_chan_decode_data_dict_entry_v1( + pki_dir, encryption_algorithm, minion_opts, master_opts +): + mockloop = MagicMock() + minion_opts.update( + { + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("minion")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + "acceptance_wait_time": 3, + "acceptance_wait_time_max": 3, + } + ) + master_opts = dict(master_opts, pki_dir=str(pki_dir.joinpath("master"))) + server = salt.channel.server.ReqServerChannel.factory(master_opts) + client = salt.channel.client.ReqChannel.factory(minion_opts, io_loop=mockloop) + dictkey = "pillar" + target = "minion" + pillar_data = {"pillar1": "meh"} + ret = server._encrypt_private( + pillar_data, + dictkey, + target, + sign_messages=False, + encryption_algorithm=encryption_algorithm, + ) + key = client.auth.get_keys() + aes = key.decrypt(ret["key"], encryption_algorithm) + pcrypt = salt.crypt.Crypticle(client.opts, aes) + ret_pillar_data = pcrypt.loads(ret[dictkey]) + assert ret_pillar_data == pillar_data + + +async def test_req_chan_decode_data_dict_entry_v2(minion_opts, master_opts, pki_dir): + mockloop = MagicMock() + minion_opts.update( + { + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("minion")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + "acceptance_wait_time": 3, + "acceptance_wait_time_max": 3, + } + ) + master_opts.update(pki_dir=str(pki_dir.joinpath("master"))) + server = salt.channel.server.ReqServerChannel.factory(master_opts) + client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=mockloop) + + dictkey = "pillar" + target = "minion" + pillar_data = {"pillar1": "meh"} + + # Mock auth and message client. + auth = client.auth + auth._crypticle = salt.crypt.Crypticle(minion_opts, AES_KEY) + client.auth = MagicMock() + client.auth.mpub = auth.mpub + client.auth.authenticated = True + client.auth.get_keys = auth.get_keys + client.auth.crypticle.dumps = auth.crypticle.dumps + client.auth.crypticle.loads = auth.crypticle.loads + client.transport = MagicMock() + + print(minion_opts["encryption_algorithm"]) + + @salt.ext.tornado.gen.coroutine + def mocksend(msg, timeout=60, tries=3): + client.transport.msg = msg + load = client.auth.crypticle.loads(msg["load"]) + ret = server._encrypt_private( + pillar_data, + dictkey, + target, + nonce=load["nonce"], + sign_messages=True, + encryption_algorithm=minion_opts["encryption_algorithm"], + signing_algorithm=minion_opts["signing_algorithm"], + ) + raise salt.ext.tornado.gen.Return(ret) + + client.transport.send = mocksend + + # Note the 'ver' value in 'load' does not represent the the 'version' sent + # in the top level of the transport's message. + load = { + "id": target, + "grains": {}, + "saltenv": "base", + "pillarenv": "base", + "pillar_override": True, + "extra_minion_data": {}, + "ver": "2", + "cmd": "_pillar", + } + ret = await client.crypted_transfer_decode_dictentry( # pylint: disable=E1121,E1123 + load, + dictkey="pillar", + ) + assert "version" in client.transport.msg + assert client.transport.msg["version"] == 2 + assert ret == {"pillar1": "meh"} + + +async def test_req_chan_decode_data_dict_entry_v2_bad_nonce( + pki_dir, minion_opts, master_opts +): + mockloop = MagicMock() + minion_opts.update( + { + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("minion")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + "acceptance_wait_time": 3, + "acceptance_wait_time_max": 3, + } + ) + master_opts.update(pki_dir=str(pki_dir.joinpath("master"))) + server = salt.channel.server.ReqServerChannel.factory(master_opts) + client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=mockloop) + + dictkey = "pillar" + badnonce = "abcdefg" + target = "minion" + pillar_data = {"pillar1": "meh"} + + # Mock auth and message client. + auth = client.auth + auth._crypticle = salt.crypt.Crypticle(minion_opts, AES_KEY) + client.auth = MagicMock() + client.auth.mpub = auth.mpub + client.auth.authenticated = True + client.auth.get_keys = auth.get_keys + client.auth.crypticle.dumps = auth.crypticle.dumps + client.auth.crypticle.loads = auth.crypticle.loads + client.transport = MagicMock() + ret = server._encrypt_private( + pillar_data, + dictkey, + target, + nonce=badnonce, + sign_messages=True, + encryption_algorithm=minion_opts["encryption_algorithm"], + signing_algorithm=minion_opts["signing_algorithm"], + ) + + @salt.ext.tornado.gen.coroutine + def mocksend(msg, timeout=60, tries=3): + client.transport.msg = msg + raise salt.ext.tornado.gen.Return(ret) + + client.transport.send = mocksend + + # Note the 'ver' value in 'load' does not represent the the 'version' sent + # in the top level of the transport's message. + load = { + "id": target, + "grains": {}, + "saltenv": "base", + "pillarenv": "base", + "pillar_override": True, + "extra_minion_data": {}, + "ver": "2", + "cmd": "_pillar", + } + + with pytest.raises(salt.crypt.AuthenticationError) as excinfo: + ret = await client.crypted_transfer_decode_dictentry( # pylint: disable=E1121,E1123 + load, + dictkey="pillar", + ) + assert "Pillar nonce verification failed." == excinfo.value.message + + +async def test_req_chan_decode_data_dict_entry_v2_bad_signature( + pki_dir, minion_opts, master_opts +): + mockloop = MagicMock() + minion_opts.update( + { + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("minion")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + "acceptance_wait_time": 3, + "acceptance_wait_time_max": 3, + } + ) + master_opts.update(pki_dir=str(pki_dir.joinpath("master"))) + server = salt.channel.server.ReqServerChannel.factory(master_opts) + client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=mockloop) + + dictkey = "pillar" + badnonce = "abcdefg" + target = "minion" + pillar_data = {"pillar1": "meh"} + + # Mock auth and message client. + auth = client.auth + auth._crypticle = salt.crypt.Crypticle(minion_opts, AES_KEY) + client.auth = MagicMock() + client.auth.mpub = auth.mpub + client.auth.authenticated = True + client.auth.get_keys = auth.get_keys + client.auth.crypticle.dumps = auth.crypticle.dumps + client.auth.crypticle.loads = auth.crypticle.loads + client.transport = MagicMock() + + @salt.ext.tornado.gen.coroutine + def mocksend(msg, timeout=60, tries=3): + client.transport.msg = msg + load = client.auth.crypticle.loads(msg["load"]) + ret = server._encrypt_private( + pillar_data, + dictkey, + target, + nonce=load["nonce"], + sign_messages=True, + encryption_algorithm=minion_opts["encryption_algorithm"], + signing_algorithm=minion_opts["signing_algorithm"], + ) + + key = client.auth.get_keys() + aes = key.decrypt(ret["key"], minion_opts["encryption_algorithm"]) + pcrypt = salt.crypt.Crypticle(client.opts, aes) + signed_msg = pcrypt.loads(ret[dictkey]) + # Changing the pillar data will cause the signature verification to + # fail. + data = salt.payload.loads(signed_msg["data"]) + data["pillar"] = {"pillar1": "bar"} + signed_msg["data"] = salt.payload.dumps(data) + ret[dictkey] = pcrypt.dumps(signed_msg) + raise salt.ext.tornado.gen.Return(ret) + + client.transport.send = mocksend + + # Note the 'ver' value in 'load' does not represent the the 'version' sent + # in the top level of the transport's message. + load = { + "id": target, + "grains": {}, + "saltenv": "base", + "pillarenv": "base", + "pillar_override": True, + "extra_minion_data": {}, + "ver": "2", + "cmd": "_pillar", + } + + with pytest.raises(salt.crypt.AuthenticationError) as excinfo: + ret = await client.crypted_transfer_decode_dictentry( # pylint: disable=E1121,E1123 + load, + dictkey="pillar", + ) + assert "Pillar payload signature failed to validate." == excinfo.value.message + + +async def test_req_chan_decode_data_dict_entry_v2_bad_key( + pki_dir, minion_opts, master_opts +): + mockloop = MagicMock() + minion_opts.update( + { + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("minion")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + "acceptance_wait_time": 3, + "acceptance_wait_time_max": 3, + } + ) + master_opts.update(pki_dir=str(pki_dir.joinpath("master"))) + server = salt.channel.server.ReqServerChannel.factory(master_opts) + client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=mockloop) + + dictkey = "pillar" + badnonce = "abcdefg" + target = "minion" + pillar_data = {"pillar1": "meh"} + + # Mock auth and message client. + auth = client.auth + auth._crypticle = salt.crypt.Crypticle(master_opts, AES_KEY) + client.auth = MagicMock() + client.auth.mpub = auth.mpub + client.auth.authenticated = True + client.auth.get_keys = auth.get_keys + client.auth.crypticle.dumps = auth.crypticle.dumps + client.auth.crypticle.loads = auth.crypticle.loads + client.transport = MagicMock() + + @salt.ext.tornado.gen.coroutine + def mocksend(msg, timeout=60, tries=3): + client.transport.msg = msg + load = client.auth.crypticle.loads(msg["load"]) + ret = server._encrypt_private( + pillar_data, + dictkey, + target, + nonce=load["nonce"], + sign_messages=True, + encryption_algorithm=minion_opts["encryption_algorithm"], + signing_algorithm=minion_opts["signing_algorithm"], + ) + + mkey = client.auth.get_keys() + aes = mkey.decrypt(ret["key"], minion_opts["encryption_algorithm"]) + pcrypt = salt.crypt.Crypticle(client.opts, aes) + signed_msg = pcrypt.loads(ret[dictkey]) + + # Now encrypt with a different key + key = salt.crypt.Crypticle.generate_key_string() + pcrypt = salt.crypt.Crypticle(master_opts, key) + pubfn = os.path.join(master_opts["pki_dir"], "minions", "minion") + pub = salt.crypt.PublicKey(pubfn) + ret[dictkey] = pcrypt.dumps(signed_msg) + key = salt.utils.stringutils.to_bytes(key) + ret["key"] = pub.encrypt(key, minion_opts["encryption_algorithm"]) + raise salt.ext.tornado.gen.Return(ret) + + client.transport.send = mocksend + + # Note the 'ver' value in 'load' does not represent the the 'version' sent + # in the top level of the transport's message. + load = { + "id": target, + "grains": {}, + "saltenv": "base", + "pillarenv": "base", + "pillar_override": True, + "extra_minion_data": {}, + "ver": "2", + "cmd": "_pillar", + } + try: + with pytest.raises(salt.crypt.AuthenticationError) as excinfo: + await client.crypted_transfer_decode_dictentry( # pylint: disable=E1121,E1123 + load, + dictkey="pillar", + ) + assert "Key verification failed." == excinfo.value.message + finally: + client.close() + server.close() + + +async def test_req_serv_auth_v1(pki_dir, minion_opts, master_opts): + minion_opts.update( + { + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("minion")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + "max_minions": 0, + "auto_accept": False, + "open_mode": False, + "key_pass": None, + "master_sign_pubkey": False, + "publish_port": 4505, + "auth_mode": 1, + } + ) + SMaster.secrets["aes"] = { + "secret": multiprocessing.Array( + ctypes.c_char, + salt.utils.stringutils.to_bytes(salt.crypt.Crypticle.generate_key_string()), + ), + "reload": salt.crypt.Crypticle.generate_key_string, + } + master_opts.update(pki_dir=str(pki_dir.joinpath("master"))) + server = salt.channel.server.ReqServerChannel.factory(master_opts) + + server.auto_key = salt.daemons.masterapi.AutoKey(server.opts) + server.cache_cli = False + server.event = salt.utils.event.get_master_event( + master_opts, master_opts["sock_dir"], listen=False + ) + server.master_key = salt.crypt.MasterKeys(server.opts) + + pub = salt.crypt.get_rsa_pub_key(str(pki_dir.joinpath("minion", "minion.pub"))) + token = salt.utils.stringutils.to_bytes(salt.crypt.Crypticle.generate_key_string()) + nonce = uuid.uuid4().hex + + # We need to read the public key with fopen otherwise the newlines might + # not match on windows. + with salt.utils.files.fopen( + str(pki_dir.joinpath("minion", "minion.pub")), "r" + ) as fp: + pub_key = salt.crypt.clean_key(fp.read()) + + load = { + "cmd": "_auth", + "id": "minion", + "token": token, + "pub": pub_key, + "enc_algo": minion_opts["encryption_algorithm"], + "sig_algo": minion_opts["signing_algorithm"], + } + ret = server._auth(load, sign_messages=False) + assert "load" not in ret + + +async def test_req_serv_auth_v2(pki_dir, minion_opts, master_opts): + minion_opts.update( + { + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("minion")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + "max_minions": 0, + "auto_accept": False, + "open_mode": False, + "key_pass": None, + "master_sign_pubkey": False, + "publish_port": 4505, + "auth_mode": 1, + } + ) + SMaster.secrets["aes"] = { + "secret": multiprocessing.Array( + ctypes.c_char, + salt.utils.stringutils.to_bytes(salt.crypt.Crypticle.generate_key_string()), + ), + "reload": salt.crypt.Crypticle.generate_key_string, + } + master_opts.update(pki_dir=str(pki_dir.joinpath("master"))) + server = salt.channel.server.ReqServerChannel.factory(master_opts) + server.auto_key = salt.daemons.masterapi.AutoKey(server.opts) + server.cache_cli = False + server.event = salt.utils.event.get_master_event( + master_opts, master_opts["sock_dir"], listen=False + ) + server.master_key = salt.crypt.MasterKeys(server.opts) + + pub = salt.crypt.get_rsa_pub_key(str(pki_dir.joinpath("minion", "minion.pub"))) + token = salt.utils.stringutils.to_bytes(salt.crypt.Crypticle.generate_key_string()) + nonce = uuid.uuid4().hex + + # We need to read the public key with fopen otherwise the newlines might + # not match on windows. + with salt.utils.files.fopen( + str(pki_dir.joinpath("minion", "minion.pub")), "r" + ) as fp: + pub_key = fp.read() + + load = { + "cmd": "_auth", + "id": "minion", + "nonce": nonce, + "token": token, + "pub": pub_key, + "enc_algo": minion_opts["encryption_algorithm"], + "sig_algo": minion_opts["signing_algorithm"], + } + ret = server._auth(load, sign_messages=True) + assert "sig" in ret + assert "load" in ret + + +async def test_req_chan_auth_v2(pki_dir, io_loop, minion_opts, master_opts): + minion_opts.update( + { + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("minion")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + "max_minions": 0, + "auto_accept": False, + "open_mode": False, + "key_pass": None, + "publish_port": 4505, + "auth_mode": 1, + "acceptance_wait_time": 3, + "acceptance_wait_time_max": 3, + } + ) + SMaster.secrets["aes"] = { + "secret": multiprocessing.Array( + ctypes.c_char, + salt.utils.stringutils.to_bytes(salt.crypt.Crypticle.generate_key_string()), + ), + "reload": salt.crypt.Crypticle.generate_key_string, + } + master_opts.update(pki_dir=str(pki_dir.joinpath("master"))) + master_opts["master_sign_pubkey"] = False + server = salt.channel.server.ReqServerChannel.factory(master_opts) + server.auto_key = salt.daemons.masterapi.AutoKey(server.opts) + server.cache_cli = False + server.event = salt.utils.event.get_master_event( + master_opts, master_opts["sock_dir"], listen=False + ) + server.master_key = salt.crypt.MasterKeys(server.opts) + minion_opts["verify_master_pubkey_sign"] = False + minion_opts["always_verify_signature"] = False + client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=io_loop) + signin_payload = client.auth.minion_sign_in_payload() + pload = client._package_load(signin_payload) + assert "version" in pload + assert pload["version"] == 2 + + ret = server._auth(pload["load"], sign_messages=True) + assert "sig" in ret + ret = client.auth.handle_signin_response(signin_payload, ret) + assert "aes" in ret + assert "master_uri" in ret + assert "publish_port" in ret + + +async def test_req_chan_auth_v2_with_master_signing( + pki_dir, io_loop, minion_opts, master_opts +): + minion_opts.update( + { + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("minion")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + "max_minions": 0, + "auto_accept": False, + "open_mode": False, + "key_pass": None, + "publish_port": 4505, + "auth_mode": 1, + "acceptance_wait_time": 3, + "acceptance_wait_time_max": 3, + } + ) + SMaster.secrets["aes"] = { + "secret": multiprocessing.Array( + ctypes.c_char, + salt.utils.stringutils.to_bytes(salt.crypt.Crypticle.generate_key_string()), + ), + "reload": salt.crypt.Crypticle.generate_key_string, + } + master_opts = dict(master_opts, pki_dir=str(pki_dir.joinpath("master"))) + master_opts["master_sign_pubkey"] = True + master_opts["master_use_pubkey_signature"] = False + master_opts["signing_key_pass"] = "" + master_opts["master_sign_key_name"] = "master_sign" + server = salt.channel.server.ReqServerChannel.factory(master_opts) + server.auto_key = salt.daemons.masterapi.AutoKey(server.opts) + server.cache_cli = False + server.event = salt.utils.event.get_master_event( + master_opts, master_opts["sock_dir"], listen=False + ) + server.master_key = salt.crypt.MasterKeys(server.opts) + minion_opts["verify_master_pubkey_sign"] = True + minion_opts["always_verify_signature"] = True + minion_opts["master_sign_key_name"] = "master_sign" + minion_opts["master"] = "master" + + assert ( + pki_dir.joinpath("minion", "minion_master.pub").read_text() + == pki_dir.joinpath("master", "master.pub").read_text() + ) + + client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=io_loop) + signin_payload = client.auth.minion_sign_in_payload() + pload = client._package_load(signin_payload) + assert "version" in pload + assert pload["version"] == 2 + + server_reply = server._auth(pload["load"], sign_messages=True) + # With version 2 we always get a clear signed response + assert "enc" in server_reply + assert server_reply["enc"] == "clear" + assert "sig" in server_reply + assert "load" in server_reply + ret = client.auth.handle_signin_response(signin_payload, server_reply) + assert "aes" in ret + assert "master_uri" in ret + assert "publish_port" in ret + + # Now create a new master key pair and try auth with it. + mapriv = pki_dir.joinpath("master", "master.pem") + mapriv.unlink() + mapriv.write_text(MASTER2_PRIV_KEY.strip()) + mapub = pki_dir.joinpath("master", "master.pub") + mapub.unlink() + mapub.write_text(MASTER2_PUB_KEY.strip()) + + server = salt.channel.server.ReqServerChannel.factory(master_opts) + server.auto_key = salt.daemons.masterapi.AutoKey(server.opts) + server.cache_cli = False + server.event = salt.utils.event.get_master_event( + master_opts, master_opts["sock_dir"], listen=False + ) + server.master_key = salt.crypt.MasterKeys(server.opts) + + signin_payload = client.auth.minion_sign_in_payload() + pload = client._package_load(signin_payload) + server_reply = server._auth(pload["load"], sign_messages=True) + ret = client.auth.handle_signin_response(signin_payload, server_reply) + + assert "aes" in ret + assert "master_uri" in ret + assert "publish_port" in ret + + assert ( + pki_dir.joinpath("minion", "minion_master.pub").read_text() + == pki_dir.joinpath("master", "master.pub").read_text() + ) + + +async def test_req_chan_auth_v2_new_minion_with_master_pub( + pki_dir, io_loop, minion_opts, master_opts +): + + pki_dir.joinpath("master", "minions", "minion").unlink() + minion_opts.update( + { + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("minion")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + "max_minions": 0, + "auto_accept": False, + "open_mode": False, + "key_pass": None, + "publish_port": 4505, + "auth_mode": 1, + "acceptance_wait_time": 3, + "acceptance_wait_time_max": 3, + } + ) + SMaster.secrets["aes"] = { + "secret": multiprocessing.Array( + ctypes.c_char, + salt.utils.stringutils.to_bytes(salt.crypt.Crypticle.generate_key_string()), + ), + "reload": salt.crypt.Crypticle.generate_key_string, + } + master_opts.update(pki_dir=str(pki_dir.joinpath("master"))) + master_opts["master_sign_pubkey"] = False + server = salt.channel.server.ReqServerChannel.factory(master_opts) + server.auto_key = salt.daemons.masterapi.AutoKey(server.opts) + server.cache_cli = False + server.event = salt.utils.event.get_master_event( + master_opts, master_opts["sock_dir"], listen=False + ) + server.master_key = salt.crypt.MasterKeys(server.opts) + minion_opts["verify_master_pubkey_sign"] = False + minion_opts["always_verify_signature"] = False + client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=io_loop) + signin_payload = client.auth.minion_sign_in_payload() + pload = client._package_load(signin_payload) + assert "version" in pload + assert pload["version"] == 2 + + ret = server._auth(pload["load"], sign_messages=True) + assert "sig" in ret + ret = client.auth.handle_signin_response(signin_payload, ret) + assert ret == "retry" + + +async def test_req_chan_auth_v2_new_minion_with_master_pub_bad_sig( + pki_dir, io_loop, minion_opts, master_opts +): + + pki_dir.joinpath("master", "minions", "minion").unlink() + + # Give the master a different key than the minion has. + mapriv = pki_dir.joinpath("master", "master.pem") + mapriv.unlink() + mapriv.write_text(MASTER2_PRIV_KEY.strip()) + mapub = pki_dir.joinpath("master", "master.pub") + mapub.unlink() + mapub.write_text(MASTER2_PUB_KEY.strip()) + + minion_opts.update( + { + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("minion")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + "max_minions": 0, + "auto_accept": False, + "open_mode": False, + "key_pass": None, + "publish_port": 4505, + "auth_mode": 1, + "acceptance_wait_time": 3, + "acceptance_wait_time_max": 3, + } + ) + SMaster.secrets["aes"] = { + "secret": multiprocessing.Array( + ctypes.c_char, + salt.utils.stringutils.to_bytes(salt.crypt.Crypticle.generate_key_string()), + ), + "reload": salt.crypt.Crypticle.generate_key_string, + } + master_opts.update( + pki_dir=str(pki_dir.joinpath("master")), master_sign_pubkey=False + ) + server = salt.channel.server.ReqServerChannel.factory(master_opts) + server.auto_key = salt.daemons.masterapi.AutoKey(server.opts) + server.cache_cli = False + server.event = salt.utils.event.get_master_event( + master_opts, master_opts["sock_dir"], listen=False + ) + server.master_key = salt.crypt.MasterKeys(server.opts) + minion_opts["verify_master_pubkey_sign"] = False + minion_opts["always_verify_signature"] = False + client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=io_loop) + signin_payload = client.auth.minion_sign_in_payload() + pload = client._package_load(signin_payload) + assert "version" in pload + assert pload["version"] == 2 + + ret = server._auth(pload["load"], sign_messages=True) + assert "sig" in ret + with pytest.raises(salt.crypt.SaltClientError, match="Invalid signature"): + ret = client.auth.handle_signin_response(signin_payload, ret) + + +async def test_req_chan_auth_v2_new_minion_without_master_pub( + minion_opts, + master_opts, + pki_dir, + io_loop, +): + + pki_dir.joinpath("master", "minions", "minion").unlink() + pki_dir.joinpath("minion", "minion_master.pub").unlink() + minion_opts.update( + { + "master_uri": "tcp://127.0.0.1:4506", + "interface": "127.0.0.1", + "ret_port": 4506, + "ipv6": False, + "sock_dir": ".", + "pki_dir": str(pki_dir.joinpath("minion")), + "id": "minion", + "__role": "minion", + "keysize": 4096, + "max_minions": 0, + "auto_accept": False, + "open_mode": False, + "key_pass": None, + "publish_port": 4505, + "auth_mode": 1, + "acceptance_wait_time": 3, + "acceptance_wait_time_max": 3, + } + ) + SMaster.secrets["aes"] = { + "secret": multiprocessing.Array( + ctypes.c_char, + salt.utils.stringutils.to_bytes(salt.crypt.Crypticle.generate_key_string()), + ), + "reload": salt.crypt.Crypticle.generate_key_string, + } + master_opts.update(pki_dir=str(pki_dir.joinpath("master"))) + master_opts["master_sign_pubkey"] = False + server = salt.channel.server.ReqServerChannel.factory(master_opts) + server.auto_key = salt.daemons.masterapi.AutoKey(server.opts) + server.cache_cli = False + server.event = salt.utils.event.get_master_event( + master_opts, master_opts["sock_dir"], listen=False + ) + server.master_key = salt.crypt.MasterKeys(server.opts) + minion_opts["verify_master_pubkey_sign"] = False + minion_opts["always_verify_signature"] = False + client = salt.channel.client.AsyncReqChannel.factory(minion_opts, io_loop=io_loop) + signin_payload = client.auth.minion_sign_in_payload() + pload = client._package_load(signin_payload) + try: + assert "version" in pload + assert pload["version"] == 2 + + ret = server._auth(pload["load"], sign_messages=True) + assert "sig" in ret + ret = client.auth.handle_signin_response(signin_payload, ret) + assert ret == "retry" + finally: + client.close() + server.close() + + async def test_req_server_garbage_request(io_loop): """ Validate invalid msgpack messages will not raise exceptions in the diff --git a/tests/pytests/unit/utils/test_atomicfile.py b/tests/pytests/unit/utils/test_atomicfile.py new file mode 100644 index 00000000000..06dfd9a5b68 --- /dev/null +++ b/tests/pytests/unit/utils/test_atomicfile.py @@ -0,0 +1,27 @@ +""" +Tests for atomicfile utility module. +""" + +import pytest + +import salt.utils.files +from salt.utils.atomicfile import atomic_open + + +@pytest.mark.skip_on_windows(reason="Not a Windows test") +def test_atomicfile_respects_umask(tmp_path): + """ + Test that creating a file using atomic_open respects the umask, instead of + creating the file with 0600 perms. + """ + new_file = tmp_path / "foo" + contents = "bar" + + # Set the umask specifically for this test so that we know what the mode of + # the created file should be. + with salt.utils.files.set_umask(0o022): + with atomic_open(str(new_file), "w") as fh_: + fh_.write(contents) + + assert new_file.read_text() == contents + assert oct(new_file.stat().st_mode)[-3:] == "644" diff --git a/tests/pytests/unit/utils/test_win_update.py b/tests/pytests/unit/utils/test_win_update.py index a221ee31952..9939428c7ca 100644 --- a/tests/pytests/unit/utils/test_win_update.py +++ b/tests/pytests/unit/utils/test_win_update.py @@ -1,14 +1,52 @@ import pytest +try: + import win32com.client + + HAS_WIN32 = True +except ImportError: + HAS_WIN32 = False + import salt.utils.win_update as win_update from tests.support.mock import MagicMock, patch pytestmark = [ pytest.mark.windows_whitelisted, pytest.mark.skip_unless_on_windows, + pytest.mark.skipif(not HAS_WIN32, reason="Requires Win32 libraries"), ] +def test_available_no_updates(): + """ + Test installed when there are no updates on the system + """ + with patch("salt.utils.winapi.Com", autospec=True), patch( + "win32com.client.Dispatch", autospec=True + ), patch.object(win_update.WindowsUpdateAgent, "refresh", autospec=True): + wua = win_update.WindowsUpdateAgent(online=False) + wua._updates = [] + + available_updates = wua.available() + + assert available_updates.updates.Add.call_count == 0 + + +def test_available_no_updates_empty_objects(): + """ + Test installed when there are no updates on the system + """ + with patch("salt.utils.winapi.Com", autospec=True), patch( + "win32com.client.Dispatch", autospec=True + ), patch.object(win_update.WindowsUpdateAgent, "refresh", autospec=True): + wua = win_update.WindowsUpdateAgent(online=False) + wua._updates = [win32com.client.CDispatch, win32com.client.CDispatch] + + available_updates = wua.available() + + assert available_updates.updates.Add.call_count == 0 + + def test_installed_no_updates(): """ Test installed when there are no updates on the system diff --git a/tests/pytests/unit/utils/verify/test_clean_path.py b/tests/pytests/unit/utils/verify/test_clean_path.py index 062821eb796..9899cbde076 100644 --- a/tests/pytests/unit/utils/verify/test_clean_path.py +++ b/tests/pytests/unit/utils/verify/test_clean_path.py @@ -3,6 +3,7 @@ salt.utils.clean_path works as expected """ import salt.utils.verify +from tests.support.mock import patch def test_clean_path_valid(tmp_path): @@ -15,3 +16,10 @@ def test_clean_path_invalid(tmp_path): path_a = str(tmp_path / "foo") path_b = str(tmp_path / "baz" / "bar") assert salt.utils.verify.clean_path(path_a, path_b) == "" + + +def test_clean_path_relative_root(tmp_path): + with patch("os.getcwd", return_value=str(tmp_path)): + path_a = "foo" + path_b = str(tmp_path / "foo" / "bar") + assert salt.utils.verify.clean_path(path_a, path_b) == path_b diff --git a/tests/support/pkg.py b/tests/support/pkg.py index 7873bf182eb..adebcd0b9c7 100644 --- a/tests/support/pkg.py +++ b/tests/support/pkg.py @@ -220,6 +220,7 @@ class SaltPkgInstall: version = self.prev_version parsed = packaging.version.parse(version) version = f"{parsed.major}.{parsed.minor}" + # ensure services stopped on Debian/Ubuntu (minic install for RedHat - non-starting) if self.distro_id in ("ubuntu", "debian"): self.stop_services() return version @@ -489,9 +490,11 @@ class SaltPkgInstall: log.debug("Installing: %s", str(pkg)) ret = self.proc.run("installer", "-pkg", str(pkg), "-target", "/") self._check_retcode(ret) + # Stop the service installed by the installer self.proc.run("launchctl", "disable", f"system/{service_name}") self.proc.run("launchctl", "bootout", "system", str(plist_file)) + elif upgrade: env = os.environ.copy() extra_args = [] @@ -589,17 +592,16 @@ class SaltPkgInstall: def stop_services(self): """ - Debian distros automatically start the services - We want to ensure our tests start with the config - settings we have set. This will also verify the expected - services are up and running. + Debian/Ubuntu distros automatically start the services on install + We want to ensure our tests start with the config settings we have set. + This will also verify the expected services are up and running. """ retval = True for service in ["salt-syndic", "salt-master", "salt-minion"]: check_run = self.proc.run("systemctl", "status", service) if check_run.returncode != 0: - # The system was not started automatically and we - # are expecting it to be on install + # The system was not started automatically and + # we are expecting it to be on install on Debian/Ubuntu systems log.debug("The service %s was not started on install.", service) retval = False else: @@ -607,6 +609,23 @@ class SaltPkgInstall: self._check_retcode(stop_service) return retval + def restart_services(self): + """ + Debian/Ubuntu distros automatically start the services + We want to ensure our tests start with the config settings we have set, + for example: after install the services are stopped (similar to RedHat not starting services on install) + This will also verify the expected services are up and running. + """ + for service in ["salt-minion", "salt-master", "salt-syndic"]: + check_run = self.proc.run("systemctl", "status", service) + log.debug( + "The restart_services status, before restart, for service %s is %s.", + service, + check_run, + ) + restart_service = self.proc.run("systemctl", "restart", service) + self._check_retcode(restart_service) + def install_previous(self, downgrade=False): """ Install previous version. This is used for @@ -710,7 +729,6 @@ class SaltPkgInstall: gpg_key = gpg_dest if relenv: gpg_key = "SALT-PROJECT-GPG-PUBKEY-2023.gpg" - download_file( f"https://repo.saltproject.io/{root_url}{distro_name}/{self.distro_version}/{arch}/{major_ver}/{gpg_key}", f"/etc/apt/keyrings/{gpg_dest}", @@ -831,14 +849,21 @@ class SaltPkgInstall: self._install_ssm_service() elif platform.is_darwin(): + if relenv and platform.is_aarch64(): + arch = "arm64" + elif platform.is_aarch64() and self.classic: + arch = "arm64" + else: + arch = "x86_64" + if self.classic: - mac_pkg = f"salt-{self.prev_version}-py3-x86_64.pkg" + mac_pkg = f"salt-{self.prev_version}-py3-{arch}.pkg" mac_pkg_url = f"https://repo.saltproject.io/osx/{mac_pkg}" else: if not relenv: - mac_pkg = f"salt-{self.prev_version}-1-macos-x86_64.pkg" + mac_pkg = f"salt-{self.prev_version}-1-macos-{arch}.pkg" else: - mac_pkg = f"salt-{self.prev_version}-py3-x86_64.pkg" + mac_pkg = f"salt-{self.prev_version}-py3-{arch}.pkg" mac_pkg_url = ( f"https://repo.saltproject.io/salt/py3/macos/{major_ver}/{mac_pkg}" ) @@ -1018,7 +1043,11 @@ class SaltPkgInstall: if self.upgrade: self.install_previous() else: + # assume downgrade, since no_install only used in these two cases self.install() + else: + self.install() + return self def __exit__(self, *_): diff --git a/tools/ci.py b/tools/ci.py index 9a713a2f557..ae3ad518239 100644 --- a/tools/ci.py +++ b/tools/ci.py @@ -771,13 +771,19 @@ def pkg_matrix( gh_event_path = os.environ.get("GITHUB_EVENT_PATH") or None if gh_event_path is None: ctx.warn("The 'GITHUB_EVENT_PATH' variable is not set.") - else: - try: - gh_event = json.loads(open(gh_event_path, encoding="utf-8").read()) - except Exception as exc: - ctx.error( - f"Could not load the GH Event payload from {gh_event_path!r}:\n", exc - ) + ctx.exit(1) + + if TYPE_CHECKING: + assert gh_event_path is not None + + try: + gh_event = json.loads(open(gh_event_path, encoding="utf-8").read()) + except Exception as exc: + ctx.error(f"Could not load the GH Event payload from {gh_event_path!r}:\n", exc) + ctx.exit(1) + + if TYPE_CHECKING: + assert gh_event is not None github_output = os.environ.get("GITHUB_OUTPUT") if github_output is None: @@ -786,28 +792,9 @@ def pkg_matrix( if TYPE_CHECKING: assert testing_releases - still_testing_3005 = False - for release_version in testing_releases: - if still_testing_3005: - break - if release_version < tools.utils.Version("3006.0"): - still_testing_3005 = True - - if still_testing_3005 is False: - ctx.error( - f"No longer testing 3005.x releases please update {__file__} " - "and remove this error and the logic above the error. There may " - "be other places that need code removed as well." - ) - ctx.exit(1) - adjusted_versions = [] for ver in testing_releases: - if ver < tools.utils.Version("3006.0"): - adjusted_versions.append((ver, "classic")) - adjusted_versions.append((ver, "tiamat")) - else: - adjusted_versions.append((ver, "relenv")) + adjusted_versions.append((ver, "relenv")) ctx.info(f"Will look for the following versions: {adjusted_versions}") # Filter out the prefixes to look under @@ -860,16 +847,6 @@ def pkg_matrix( ] for version, backend in adjusted_versions: - if ( - distro_slug.startswith(("macos-", "debian-", "ubuntu-")) - or version.major < 3006 - ): - # XXX: Temporarily skip problematic tests - ctx.warn( - f"Temporary skip builds on {distro_slug} for version {version} with backend {backend}" - ) - continue - prefix = prefixes[backend] # TODO: Remove this after 3009.0 if backend == "relenv" and version >= tools.utils.Version("3006.5"): @@ -886,10 +863,9 @@ def pkg_matrix( # key_filter = f"Contents[?contains(Key, '{version}')] | [?ends_with(Key, '.msi')]" continue elif pkg_type == "NSIS": - # XXX: Temporarily skip problematic tests - # key_filter = ( - # f"Contents[?contains(Key, '{version}')] | [?ends_with(Key, '.exe')]" - # ) + key_filter = ( + f"Contents[?contains(Key, '{version}')] | [?ends_with(Key, '.exe')]" + ) continue objects = list(page_iterator.search(key_filter)) # Testing using `any` because sometimes the paginator returns `[None]` @@ -917,8 +893,7 @@ def pkg_matrix( ctx.print(" * ", entry, soft_wrap=True) if ( - gh_event is not None - and gh_event["repository"]["fork"] is True + gh_event["repository"]["fork"] is True and "macos" in distro_slug and "arm64" in distro_slug ): @@ -1393,13 +1368,11 @@ def get_testing_releases( majors = sorted( list( { + # We aren't testing upgrades from anything before 3006.0 + # and we don't want to test 3007.? on the 3006.x branch version.major for version in releases - # We aren't testing upgrades from anything before - # 3006.0 except the latest 3005.x - if version.major >= 3005 - # We don't want to test 3007.? on the 3006.x branch - and version.major <= parsed_salt_version.major + if version.major > 3005 and version.major <= parsed_salt_version.major } ) )[-num_major_versions:] diff --git a/tools/docs.py b/tools/docs.py index 3471ea1e8f2..f62d1aa6a60 100644 --- a/tools/docs.py +++ b/tools/docs.py @@ -146,51 +146,6 @@ def html( ) -@docs.command( - name="epub", - arguments={ - "no_clean": { - "help": "Don't cleanup prior to building", - }, - "no_color": { - "help": "Disable colored output.", - }, - }, -) -def epub(ctx: Context, no_clean: bool = False, no_color: bool = False): - if no_clean is False: - ctx.run("make", "clean", cwd="doc/", check=True) - opts = [ - "-j", - "auto", - "--keep-going", - ] - if no_color is False: - opts.append("--color") - ctx.run( - "make", - "epub", - f"SPHINXOPTS={' '.join(opts)}", - cwd="doc/", - check=True, - ) - - artifact = tools.utils.REPO_ROOT / "doc" / "_build" / "epub" / "Salt.epub" - if "LATEST_RELEASE" in os.environ: - shutil.move( - artifact, artifact.parent / f"Salt-{os.environ['LATEST_RELEASE']}.epub" - ) - artifact = artifact.parent / f"Salt-{os.environ['LATEST_RELEASE']}.epub" - github_output = os.environ.get("GITHUB_OUTPUT") - if github_output is not None: - with open(github_output, "a", encoding="utf-8") as wfh: - wfh.write( - "has-artifacts=true\n" - f"artifact-name={artifact.resolve().name}\n" - f"artifact-path={artifact.resolve()}\n" - ) - - @docs.command( name="pdf", arguments={