diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index b70b84df5b3..0f61ba09040 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -33,7 +33,7 @@ jobs: ) steps: - name: Backport Action - uses: sqren/backport-github-action@v8.9.7 + uses: sorenlouv/backport-github-action@v8.9.7 with: github_token: ${{ secrets.GITHUB_TOKEN }} auto_backport_label_prefix: "backport:" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c5f0f048987..2d92cfc8da2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -448,7 +448,7 @@ jobs: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" self-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['self-hosted'] }} github-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['github-hosted'] }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" build-salt-onedir: @@ -464,7 +464,7 @@ jobs: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" self-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['self-hosted'] }} github-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['github-hosted'] }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" build-pkgs-onedir: @@ -477,7 +477,7 @@ jobs: with: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" cache-prefix: ${{ needs.prepare-workflow.outputs.cache-seed }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" source: "onedir" @@ -491,7 +491,7 @@ jobs: with: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" cache-prefix: ${{ needs.prepare-workflow.outputs.cache-seed }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" source: "src" build-ci-deps: diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 130612562ae..a192568679a 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -500,7 +500,7 @@ jobs: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" self-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['self-hosted'] }} github-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['github-hosted'] }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" build-salt-onedir: @@ -516,7 +516,7 @@ jobs: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" self-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['self-hosted'] }} github-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['github-hosted'] }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" build-pkgs-onedir: @@ -529,7 +529,7 @@ jobs: with: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" cache-prefix: ${{ needs.prepare-workflow.outputs.cache-seed }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" source: "onedir" environment: nightly @@ -547,7 +547,7 @@ jobs: with: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" cache-prefix: ${{ needs.prepare-workflow.outputs.cache-seed }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" source: "src" environment: nightly diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml index 358064c67ea..177055929ea 100644 --- a/.github/workflows/scheduled.yml +++ b/.github/workflows/scheduled.yml @@ -490,7 +490,7 @@ jobs: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" self-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['self-hosted'] }} github-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['github-hosted'] }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" build-salt-onedir: @@ -506,7 +506,7 @@ jobs: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" self-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['self-hosted'] }} github-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['github-hosted'] }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" build-pkgs-onedir: @@ -519,7 +519,7 @@ jobs: with: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" cache-prefix: ${{ needs.prepare-workflow.outputs.cache-seed }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" source: "onedir" @@ -533,7 +533,7 @@ jobs: with: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" cache-prefix: ${{ needs.prepare-workflow.outputs.cache-seed }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" source: "src" build-ci-deps: diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 2d50fbf368a..5bd828d5761 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -487,7 +487,7 @@ jobs: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" self-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['self-hosted'] }} github-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['github-hosted'] }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" build-salt-onedir: @@ -503,7 +503,7 @@ jobs: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" self-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['self-hosted'] }} github-hosted-runners: ${{ fromJSON(needs.prepare-workflow.outputs.runners)['github-hosted'] }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" build-pkgs-onedir: @@ -516,7 +516,7 @@ jobs: with: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" cache-prefix: ${{ needs.prepare-workflow.outputs.cache-seed }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" source: "onedir" environment: staging @@ -534,7 +534,7 @@ jobs: with: salt-version: "${{ needs.prepare-workflow.outputs.salt-version }}" cache-prefix: ${{ needs.prepare-workflow.outputs.cache-seed }} - relenv-version: "0.14.2" + relenv-version: "0.15.1" python-version: "3.10.13" source: "src" environment: staging diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b5d0a8ffaa6..4c32ac1beb0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -45,7 +45,7 @@ repos: salt/ext/.* )$ - - repo: https://github.com/s0undt3ch/python-tools-scripts + - repo: https://github.com/saltstack/python-tools-scripts rev: "0.18.6" hooks: - id: tools @@ -1416,7 +1416,7 @@ repos: hooks: - id: remove-import-headers - - repo: https://github.com/s0undt3ch/salt-rewrite + - repo: https://github.com/saltstack/salt-rewrite # Automatically rewrite code with known rules rev: 2.5.2 hooks: diff --git a/CHANGELOG.md b/CHANGELOG.md index b04a48b26b4..f69ebad42f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -179,6 +179,61 @@ Versions are `MAJOR.PATCH`. - Update to `gitpython>=3.1.35` due to https://github.com/advisories/GHSA-wfm5-v35h-vwf4 and https://github.com/advisories/GHSA-cwvm-v4w8-q58c [#65137](https://github.com/saltstack/salt/issues/65137) +## 3006.7 (2024-02-20) + + +### Deprecated + +- Deprecate and stop using ``salt.features`` [#65951](https://github.com/saltstack/salt/issues/65951) + + +### Changed + +- Change module search path priority, so Salt extensions can be overridden by syncable modules and module_dirs. You can switch back to the old logic by setting features.enable_deprecated_module_search_path_priority to true, but it will be removed in Salt 3008. [#65938](https://github.com/saltstack/salt/issues/65938) + + +### Fixed + +- 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) +- Fixed an issue when keys didn't match because of line endings [#52289](https://github.com/saltstack/salt/issues/52289) +- Corrected encoding of credentials for use with Artifactory [#63063](https://github.com/saltstack/salt/issues/63063) +- Use `send_multipart` instead of `send` when sending multipart message. [#65018](https://github.com/saltstack/salt/issues/65018) +- Fix an issue where the minion would crash on Windows if some of the grains + failed to resolve [#65154](https://github.com/saltstack/salt/issues/65154) +- Fix issue with openscap when the error was outside the expected scope. It now + returns failed with the error code and the error [#65193](https://github.com/saltstack/salt/issues/65193) +- Upgrade relenv to 0.15.0 to fix namespaced packages installed by salt-pip [#65433](https://github.com/saltstack/salt/issues/65433) +- Fix regression of fileclient re-use when rendering sls pillars and states [#65450](https://github.com/saltstack/salt/issues/65450) +- Fixes the s3fs backend computing the local cache's files with the wrong hash type [#65589](https://github.com/saltstack/salt/issues/65589) +- Fixed Salt-SSH pillar rendering and state rendering with nested SSH calls when called via saltutil.cmd or in an orchestration [#65670](https://github.com/saltstack/salt/issues/65670) +- Fix boto execution module loading [#65691](https://github.com/saltstack/salt/issues/65691) +- Removed PR 65185 changes since incomplete solution [#65692](https://github.com/saltstack/salt/issues/65692) +- catch only ret/ events not all returning events. [#65727](https://github.com/saltstack/salt/issues/65727) +- Fix nonsensical time in fileclient timeout error. [#65752](https://github.com/saltstack/salt/issues/65752) +- Fixes an issue when reading/modifying ini files that contain unicode characters [#65777](https://github.com/saltstack/salt/issues/65777) +- added https proxy to the list of proxies so that requests knows what to do with https based proxies [#65824](https://github.com/saltstack/salt/issues/65824) +- Ensure minion channels are closed on any master connection error. [#65932](https://github.com/saltstack/salt/issues/65932) +- Fixed issue where Salt can't find libcrypto when pip installed from a cloned repo [#65954](https://github.com/saltstack/salt/issues/65954) +- Fix RPM package systemd scriptlets to make RPM packages more universal [#65987](https://github.com/saltstack/salt/issues/65987) +- Fixed an issue where fileclient requests during Pillar rendering cause + fileserver backends to be needlessly refreshed. [#65990](https://github.com/saltstack/salt/issues/65990) +- Fix exceptions being set on futures that are already done in ZeroMQ transport [#66006](https://github.com/saltstack/salt/issues/66006) +- Use hmac compare_digest method in hashutil module to mitigate potential timing attacks [#66041](https://github.com/saltstack/salt/issues/66041) +- Fix request channel default timeout regression. In 3006.5 it was changed from + 60 to 30 and is now set back to 60 by default. [#66061](https://github.com/saltstack/salt/issues/66061) +- Upgrade relenv to 0.15.1 to fix debugpy support. [#66094](https://github.com/saltstack/salt/issues/66094) + + +### Security + +- Bump to ``cryptography==42.0.0`` due to https://github.com/advisories/GHSA-3ww4-gg4f-jr7f + + In the process, we were also required to update to ``pyOpenSSL==24.0.0`` [#66004](https://github.com/saltstack/salt/issues/66004) +- Bump to `cryptography==42.0.3` due to https://github.com/advisories/GHSA-3ww4-gg4f-jr7f [#66090](https://github.com/saltstack/salt/issues/66090) + + ## 3006.6 (2024-01-26) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index cae5a62bbbd..9fc693dfd09 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -88,7 +88,7 @@ version of Python: :: - pyenv install 3.7.0 + pyenv install 3.9.18 If that fails, don't panic! You're probably just missing some build dependencies. Check out `pyenv common build @@ -99,7 +99,7 @@ new virtual environment with this command: :: - pyenv virtualenv 3.7.0 salt + pyenv virtualenv 3.9.18 salt Then activate it: @@ -321,8 +321,8 @@ documentation: :: - pyenv install 3.7.15 - pyenv virtualenv 3.7.15 salt-docs + pyenv install 3.9.18 + pyenv virtualenv 3.9.18 salt-docs echo 'salt-docs' > .python-version #. Activate `pyenv` if it's not auto-activated: @@ -477,7 +477,7 @@ meaningful and complete! *Typically* the best tests for Salt are going to be unit tests. Testing is `a whole topic on its own `__, But you may also want to write functional or integration tests. You'll -find those in the ``salt/tests`` directory. +find those in the ``tests/`` directory. When you're thinking about tests to write, the most important thing to keep in mind is, “What, exactly, am I testing?” When a test fails, you diff --git a/changelog/52289.fixed.md b/changelog/52289.fixed.md deleted file mode 100644 index 50d5aaf516a..00000000000 --- a/changelog/52289.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed an issue when keys didn't match because of line endings diff --git a/changelog/59037.added.md b/changelog/59037.added.md new file mode 100644 index 00000000000..6d74b4ba63c --- /dev/null +++ b/changelog/59037.added.md @@ -0,0 +1 @@ +Added a state (win_task) for managing scheduled tasks on Windows diff --git a/changelog/63063.fixed.md b/changelog/63063.fixed.md deleted file mode 100644 index 6f26fc11abd..00000000000 --- a/changelog/63063.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Corrected encoding of credentials for use with Artifactory diff --git a/changelog/65018.fixed.md b/changelog/65018.fixed.md deleted file mode 100644 index c719aac9f99..00000000000 --- a/changelog/65018.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Use `send_multipart` instead of `send` when sending multipart message. diff --git a/changelog/65193.fixed.md b/changelog/65193.fixed.md deleted file mode 100644 index 48a7e76e461..00000000000 --- a/changelog/65193.fixed.md +++ /dev/null @@ -1,2 +0,0 @@ -Fix issue with openscap when the error was outside the expected scope. It now -returns failed with the error code and the error diff --git a/changelog/65670.fixed.md b/changelog/65670.fixed.md deleted file mode 100644 index 54728d69d43..00000000000 --- a/changelog/65670.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixed Salt-SSH pillar rendering and state rendering with nested SSH calls when called via saltutil.cmd or in an orchestration diff --git a/changelog/65691.fixed.md b/changelog/65691.fixed.md deleted file mode 100644 index 3b5192f80c0..00000000000 --- a/changelog/65691.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fix boto execution module loading diff --git a/changelog/65692.fixed.md b/changelog/65692.fixed.md deleted file mode 100644 index b4eef6c93d4..00000000000 --- a/changelog/65692.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Removed PR 65185 changes since incomplete solution diff --git a/changelog/65752.fixed.md b/changelog/65752.fixed.md deleted file mode 100644 index f611c5e65ec..00000000000 --- a/changelog/65752.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fix nonsensical time in fileclient timeout error. diff --git a/changelog/65777.fixed.md b/changelog/65777.fixed.md deleted file mode 100644 index 3427b468878..00000000000 --- a/changelog/65777.fixed.md +++ /dev/null @@ -1 +0,0 @@ -Fixes an issue when reading/modifying ini files that contain unicode characters diff --git a/changelog/65824.fixed.md b/changelog/65824.fixed.md deleted file mode 100644 index 213f3f505fb..00000000000 --- a/changelog/65824.fixed.md +++ /dev/null @@ -1 +0,0 @@ -added https proxy to the list of proxies so that requests knows what to do with https based proxies diff --git a/changelog/65938.changed.md b/changelog/65938.changed.md deleted file mode 100644 index a96e39b485d..00000000000 --- a/changelog/65938.changed.md +++ /dev/null @@ -1 +0,0 @@ -Change module search path priority, so Salt extensions can be overridden by syncable modules and module_dirs. You can switch back to the old logic by setting features.enable_deprecated_module_search_path_priority to true, but it will be removed in Salt 3008. diff --git a/changelog/65951.deprecated.md b/changelog/65951.deprecated.md deleted file mode 100644 index 833f9bf478d..00000000000 --- a/changelog/65951.deprecated.md +++ /dev/null @@ -1 +0,0 @@ -Deprecate and stop using ``salt.features`` diff --git a/changelog/65986.deprecated.md b/changelog/65986.deprecated.md new file mode 100644 index 00000000000..582631a4100 --- /dev/null +++ b/changelog/65986.deprecated.md @@ -0,0 +1,8 @@ +Removed deprecated code: + +* All of ``salt/log/`` which has been on a deprecation path for a long time. +* Some of the logging handlers found in ``salt/_logging/handlers`` have been removed since the standard library provides + them. +* Removed the deprecated ``salt/modules/cassandra_mod.py`` module and any tests for it. +* Removed the deprecated ``salt/returners/cassandra_return.py`` module and any tests for it. +* Removed the deprecated ``salt/returners/django_return.py`` module and any tests for it. diff --git a/cicd/shared-gh-workflows-context.yml b/cicd/shared-gh-workflows-context.yml index e8563b342f0..2f55b752ddc 100644 --- a/cicd/shared-gh-workflows-context.yml +++ b/cicd/shared-gh-workflows-context.yml @@ -1,6 +1,6 @@ nox_version: "2022.8.7" python_version: "3.10.13" -relenv_version: "0.14.2" +relenv_version: "0.15.1" release_branches: - "3006.x" - "3007.x" diff --git a/doc/man/salt-api.1 b/doc/man/salt-api.1 index ef5fc5ed68c..f82b64a4acf 100644 --- a/doc/man/salt-api.1 +++ b/doc/man/salt-api.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SALT-API" "1" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SALT-API" "1" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME salt-api \- salt-api Command .sp diff --git a/doc/man/salt-call.1 b/doc/man/salt-call.1 index 5854684c3ce..01a8c6c5900 100644 --- a/doc/man/salt-call.1 +++ b/doc/man/salt-call.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SALT-CALL" "1" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SALT-CALL" "1" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME salt-call \- salt-call Documentation .SH SYNOPSIS diff --git a/doc/man/salt-cloud.1 b/doc/man/salt-cloud.1 index 75365f7ea89..e2c57db40fc 100644 --- a/doc/man/salt-cloud.1 +++ b/doc/man/salt-cloud.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SALT-CLOUD" "1" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SALT-CLOUD" "1" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME salt-cloud \- Salt Cloud Command .sp diff --git a/doc/man/salt-cp.1 b/doc/man/salt-cp.1 index f0bc77a539c..bbd76b69121 100644 --- a/doc/man/salt-cp.1 +++ b/doc/man/salt-cp.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SALT-CP" "1" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SALT-CP" "1" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME salt-cp \- salt-cp Documentation .sp diff --git a/doc/man/salt-key.1 b/doc/man/salt-key.1 index 35b6d1aa3d3..c6de3044097 100644 --- a/doc/man/salt-key.1 +++ b/doc/man/salt-key.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SALT-KEY" "1" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SALT-KEY" "1" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME salt-key \- salt-key Documentation .SH SYNOPSIS diff --git a/doc/man/salt-master.1 b/doc/man/salt-master.1 index 2e3e0b5a8de..e7c04e2c15f 100644 --- a/doc/man/salt-master.1 +++ b/doc/man/salt-master.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SALT-MASTER" "1" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SALT-MASTER" "1" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME salt-master \- salt-master Documentation .sp diff --git a/doc/man/salt-minion.1 b/doc/man/salt-minion.1 index 1be877af479..70e2f1879c7 100644 --- a/doc/man/salt-minion.1 +++ b/doc/man/salt-minion.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SALT-MINION" "1" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SALT-MINION" "1" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME salt-minion \- salt-minion Documentation .sp diff --git a/doc/man/salt-proxy.1 b/doc/man/salt-proxy.1 index c7ce2d85acb..93bdd1f6fae 100644 --- a/doc/man/salt-proxy.1 +++ b/doc/man/salt-proxy.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SALT-PROXY" "1" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SALT-PROXY" "1" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME salt-proxy \- salt-proxy Documentation .sp diff --git a/doc/man/salt-run.1 b/doc/man/salt-run.1 index e968134ca1c..5231095e460 100644 --- a/doc/man/salt-run.1 +++ b/doc/man/salt-run.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SALT-RUN" "1" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SALT-RUN" "1" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME salt-run \- salt-run Documentation .sp diff --git a/doc/man/salt-ssh.1 b/doc/man/salt-ssh.1 index 1ea3976ae10..1835a0ba620 100644 --- a/doc/man/salt-ssh.1 +++ b/doc/man/salt-ssh.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SALT-SSH" "1" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SALT-SSH" "1" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME salt-ssh \- salt-ssh Documentation .SH SYNOPSIS diff --git a/doc/man/salt-syndic.1 b/doc/man/salt-syndic.1 index b006b33bbb8..652fbeb0c94 100644 --- a/doc/man/salt-syndic.1 +++ b/doc/man/salt-syndic.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SALT-SYNDIC" "1" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SALT-SYNDIC" "1" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME salt-syndic \- salt-syndic Documentation .sp diff --git a/doc/man/salt.1 b/doc/man/salt.1 index 9197a01fd3f..5faa32466d7 100644 --- a/doc/man/salt.1 +++ b/doc/man/salt.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SALT" "1" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SALT" "1" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME salt \- salt .SH SYNOPSIS diff --git a/doc/man/salt.7 b/doc/man/salt.7 index 88d600d0fcc..108bbdf6bf4 100644 --- a/doc/man/salt.7 +++ b/doc/man/salt.7 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SALT" "7" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SALT" "7" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME salt \- Salt Documentation .SH SALT PROJECT @@ -725,7 +725,7 @@ version of Python: .sp .nf .ft C -pyenv install 3.7.0 +pyenv install 3.9.18 .ft P .fi .UNINDENT @@ -742,7 +742,7 @@ new virtual environment with this command: .sp .nf .ft C -pyenv virtualenv 3.7.0 salt +pyenv virtualenv 3.9.18 salt .ft P .fi .UNINDENT @@ -1016,8 +1016,8 @@ you\(aqll need to run 3.9 or earlier. For example: .sp .nf .ft C -pyenv install 3.7.15 -pyenv virtualenv 3.7.15 salt\-docs +pyenv install 3.9.18 +pyenv virtualenv 3.9.18 salt\-docs echo \(aqsalt\-docs\(aq > .python\-version .ft P .fi @@ -1256,7 +1256,7 @@ meaningful and complete! \fITypically\fP the best tests for Salt are going to be unit tests. Testing is \fI\%a whole topic on its own\fP, But you may also want to write functional or integration tests. You\(aqll -find those in the \fBsalt/tests\fP directory. +find those in the \fBtests/\fP directory. .sp When you\(aqre thinking about tests to write, the most important thing to keep in mind is, “What, exactly, am I testing?” When a test fails, you @@ -1558,6 +1558,133 @@ For this, you would be able to install with: .fi .UNINDENT .UNINDENT +.SH SALT PROJECT MAINTENANCE POLICIES +.sp +This document explains the current project maintenance policies. The goal of +these policies are to reduce the maintenance burden on core maintainers of the +Salt Project and to encourage more active engagement from the Salt community. +.INDENT 0.0 +.IP \(bu 2 +\fI\%Issue management\fP +.IP \(bu 2 +\fI\%Pull request management\fP +.IP \(bu 2 +\fI\%Salt Enhancement Proposals (SEP) process\fP +.UNINDENT +.SS Issue management +.sp +Issues for the Salt Project are critical to Salt community communication and to +find and resolve issues in the Salt Project. As such, the issue tracker needs to +be kept clean and current to the currently supported releases of Salt. They also +need to be free of feature requests, arguments, and trolling. +.sp +We have decided to update our issue policy to be similar to RedHat community +project policies. +.sp +Community members who repeatedly violate these policies are subject to bans. +.INDENT 0.0 +.IP 1. 3 +All issues that were not opened against a currently supported release of Salt +will be closed. +.INDENT 3.0 +.IP \(bu 2 +When an old release of Salt is marked out of support, all issues opened +against the now defunct release will be closed. +.IP \(bu 2 +If the issue is still present in the current release of Salt, submit a new +issue. Do not re\-open the old issue after it has been closed. +.IP \(bu 2 +When opening a new issue that was a bug in a previous release of Salt, you +must validate it against a currently supported release of Salt for +consideration. Issues that do not show the problem against a current +release will be closed without consideration. +.UNINDENT +.IP 2. 3 +Only defects can be submitted to the issue tracker. +.INDENT 3.0 +.IP \(bu 2 +Feature requests without a PR will be immediately closed. +.IP \(bu 2 +Feature requests must be designated as a feature being developed and owned +by the issue submitter and assigned to a release. Otherwise they will be +immediately closed. +.IP \(bu 2 +Discussions about features can be held in the GitHub +\fI\%Discussions\fP tab or in +the community \fI\%Open Hour\fP\&. +.IP \(bu 2 +Questions will be immediately closed. +.UNINDENT +.IP 3. 3 +Issues must submit sufficient information. +.INDENT 3.0 +.IP \(bu 2 +Issues must follow the relevant template for information. +.IP \(bu 2 +Issues that do not give sufficient information about the nature of the +issue \fBand how to reproduce the issue\fP will be immediately closed. +.IP \(bu 2 +Issues that do not comply will be immediately closed. +.UNINDENT +.UNINDENT +.SS Pull request management +.sp +The Salt pull request (PR) queue has been a challenge to maintain for the entire +life of the project. This is in large part due to the incredibly active and +vibrant community around Salt. +.sp +Unfortunately, it has proven to be too much for the core team and the greater +Salt community to manage. As such, we deem it necessary to make fundamental +changes to how we manage the PR queue: +.INDENT 0.0 +.IP 1. 3 +All PRs opened against releases of Salt that are no longer supported will be +closed immediately. +.IP 2. 3 +Closed PRs can be resubmitted, NOT re\-opened. +.IP 3. 3 +PRs need to provide full tests for all of the code affected, regardless of +whether the PR author wrote the code affected. +.IP 4. 3 +PR tests need to be written using the current test mechanism (pytest). +.IP 5. 3 +PRs need to pass tests. +.IP 6. 3 +PRs must NOT increase the overall test time by a noticeable length. +.IP 7. 3 +PRs must NOT add new plugins directly to Salt unless sanctioned by the Salt +core team. New plugins should be made into Salt Extensions. +.IP 8. 3 +PRs that have not been updated due to inactivity will be closed. Inactivity +is determined by a lack of submitter activity for the space of 1 month. +.IP 9. 3 +PR tests should always maintain or increase total code coverage. +.UNINDENT +.SS Salt Enhancement Proposals (SEP) process +.sp +\fBA message from Thomas Hatch, creator of Salt:\fP +.sp +In 2019, we decided to create a community process to discuss and review Salt +Enhancement Proposals (SEPs). Unfortunately, I feel that this process has not +proven to be an effective way to solve the core issues around Salt Enhancements. +Overall, the Salt enhancement process has proven itself to be more of a burden +than an accelerant to Salt stability, security, and progress. As such, I feel +that the current optimal course of action is to shut the process down. +.sp +Instead of the Salt Enhancement Proposal process, we will add a time in the +\fI\%Open Hour\fP for people to present ideas and +concepts to better understand if they are worth their effort to develop. +Extensive documentation around more intrusive or involved enhancements should +be included in pull requests (PRs). Conversations about enhancements can also be +held in the \fI\%Discussions\fP tab +in GitHub. +.sp +By migrating the conversation into the PR process, we ensure that we are only +reviewing viable proposals instead of being burdened with requests that the core +team is expected to fulfill. +.sp +Effective immediately (January 2024), we are archiving and freezing the SEP +repo. .SH INSTALLATION .sp See the \fI\%Salt Install Guide\fP @@ -194368,7 +194495,7 @@ Passes through all the parameters described in the \fI\%utils.http.query function\fP: .INDENT 7.0 .TP -.B salt.utils.http.query(url, method=\(aqGET\(aq, params=None, data=None, data_file=None, header_dict=None, header_list=None, header_file=None, username=None, password=None, auth=None, decode=False, decode_type=\(aqauto\(aq, status=False, headers=False, text=False, cookies=None, cookie_jar=None, cookie_format=\(aqlwp\(aq, persist_session=False, session_cookie_jar=None, data_render=False, data_renderer=None, header_render=False, header_renderer=None, template_dict=None, test=False, test_url=None, node=\(aqminion\(aq, port=80, opts=None, backend=None, ca_bundle=None, verify_ssl=None, cert=None, text_out=None, headers_out=None, decode_out=None, stream=False, streaming_callback=None, header_callback=None, handle=False, agent=\(aqSalt/3006.6\(aq, hide_fields=None, raise_error=True, formdata=False, formdata_fieldname=None, formdata_filename=None, decode_body=True, **kwargs) +.B salt.utils.http.query(url, method=\(aqGET\(aq, params=None, data=None, data_file=None, header_dict=None, header_list=None, header_file=None, username=None, password=None, auth=None, decode=False, decode_type=\(aqauto\(aq, status=False, headers=False, text=False, cookies=None, cookie_jar=None, cookie_format=\(aqlwp\(aq, persist_session=False, session_cookie_jar=None, data_render=False, data_renderer=None, header_render=False, header_renderer=None, template_dict=None, test=False, test_url=None, node=\(aqminion\(aq, port=80, opts=None, backend=None, ca_bundle=None, verify_ssl=None, cert=None, text_out=None, headers_out=None, decode_out=None, stream=False, streaming_callback=None, header_callback=None, handle=False, agent=\(aqSalt/3006.7\(aq, hide_fields=None, raise_error=True, formdata=False, formdata_fieldname=None, formdata_filename=None, decode_body=True, **kwargs) Query a resource, and decode the return data .UNINDENT .INDENT 7.0 @@ -197100,8 +197227,46 @@ all (for example /etc/sysctl.conf) .INDENT 0.0 .TP -.B salt.modules.ini_manage.get_ini(file_name, separator=\(aq=\(aq) -Retrieve whole structure from an ini file and return it as dictionary. +.B salt.modules.ini_manage.get_ini(file_name, separator=\(aq=\(aq, encoding=None) +Retrieve the whole structure from an ini file and return it as a dictionary. +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBfile_name\fP (\fI\%str\fP) \-\- The full path to the ini file. +.IP \(bu 2 +\fBseparator\fP (\fI\%str\fP) \-\- +.sp +The character used to separate keys and values. Standard ini files +use the \(dq=\(dq character. The default is \fB=\fP\&. +.sp +New in version 2016.11.0. + + +.IP \(bu 2 +\fBencoding\fP (\fI\%str\fP) \-\- +.sp +A string value representing encoding of the target ini file. If +\fBNone\fP is passed, it uses the system default which is likely +\fButf\-8\fP\&. Default is \fBNone\fP +.sp +New in version 3006.6. + + +.UNINDENT +.TP +.B Returns +.INDENT 7.0 +.TP +.B A dictionary containing the sections along with the values and +names contained in each section +.UNINDENT + +.TP +.B Return type +\fI\%dict\fP +.UNINDENT .sp API Example: .INDENT 7.0 @@ -197110,9 +197275,8 @@ API Example: .nf .ft C import salt.client -with salt.client.giet_local_client() as sc: - sc.cmd(\(aqtarget\(aq, \(aqini.get_ini\(aq, - [path_to_ini_file]) +with salt.client.get_local_client() as sc: + sc.cmd(\(aqtarget\(aq, \(aqini.get_ini\(aq, [path_to_ini_file]) .ft P .fi .UNINDENT @@ -197132,9 +197296,52 @@ salt \(aq*\(aq ini.get_ini /path/to/ini .UNINDENT .INDENT 0.0 .TP -.B salt.modules.ini_manage.get_option(file_name, section, option, separator=\(aq=\(aq) +.B salt.modules.ini_manage.get_option(file_name, section, option, separator=\(aq=\(aq, encoding=None) Get value of a key from a section in an ini file. Returns \fBNone\fP if no matching key was found. +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBfile_name\fP (\fI\%str\fP) \-\- The full path to the ini file. +.IP \(bu 2 +\fBsection\fP (\fI\%str\fP) \-\- A string value representing the section of the ini that the option +is in. If the option is not in a section, leave this empty. +.IP \(bu 2 +\fBoption\fP (\fI\%str\fP) \-\- A string value representing the option to search for. +.IP \(bu 2 +\fBseparator\fP (\fI\%str\fP) \-\- +.sp +The character used to separate keys and values. Standard ini files +use the \(dq=\(dq character. The default is \fB=\fP\&. +.sp +New in version 2016.11.0. + + +.IP \(bu 2 +\fBencoding\fP (\fI\%str\fP) \-\- +.sp +A string value representing encoding of the target ini file. If +\fBNone\fP is passed, it uses the system default which is likely +\fButf\-8\fP\&. Default is \fBNone\fP +.sp +New in version 3006.6. + + +.UNINDENT +.TP +.B Returns +.INDENT 7.0 +.TP +.B The value as defined in the ini file, or \fBNone\fP if empty or not +found +.UNINDENT + +.TP +.B Return type +\fI\%str\fP +.UNINDENT .sp API Example: .INDENT 7.0 @@ -197144,8 +197351,7 @@ API Example: .ft C import salt.client with salt.client.get_local_client() as sc: - sc.cmd(\(aqtarget\(aq, \(aqini.get_option\(aq, - [path_to_ini_file, section_name, option]) + sc.cmd(\(aqtarget\(aq, \(aqini.get_option\(aq, [path_to_ini_file, section_name, option]) .ft P .fi .UNINDENT @@ -197165,9 +197371,50 @@ salt \(aq*\(aq ini.get_option /path/to/ini section_name option_name .UNINDENT .INDENT 0.0 .TP -.B salt.modules.ini_manage.get_section(file_name, section, separator=\(aq=\(aq) -Retrieve a section from an ini file. Returns the section as dictionary. If +.B salt.modules.ini_manage.get_section(file_name, section, separator=\(aq=\(aq, encoding=None) +Retrieve a section from an ini file. Returns the section as a dictionary. If the section is not found, an empty dictionary is returned. +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBfile_name\fP (\fI\%str\fP) \-\- The full path to the ini file. +.IP \(bu 2 +\fBsection\fP (\fI\%str\fP) \-\- A string value representing name of the section to search for. +.IP \(bu 2 +\fBseparator\fP (\fI\%str\fP) \-\- +.sp +The character used to separate keys and values. Standard ini files +use the \(dq=\(dq character. The default is \fB=\fP\&. +.sp +New in version 2016.11.0. + + +.IP \(bu 2 +\fBencoding\fP (\fI\%str\fP) \-\- +.sp +A string value representing encoding of the target ini file. If +\fBNone\fP is passed, it uses the system default which is likely +\fButf\-8\fP\&. Default is \fBNone\fP +.sp +New in version 3006.6. + + +.UNINDENT +.TP +.B Returns +.INDENT 7.0 +.TP +.B A dictionary containing the names and values of all items in the +section of the ini file. If the section is not found, an empty +dictionary is returned +.UNINDENT + +.TP +.B Return type +\fI\%dict\fP +.UNINDENT .sp API Example: .INDENT 7.0 @@ -197177,8 +197424,7 @@ API Example: .ft C import salt.client with salt.client.get_local_client() as sc: - sc.cmd(\(aqtarget\(aq, \(aqini.get_section\(aq, - [path_to_ini_file, section_name]) + sc.cmd(\(aqtarget\(aq, \(aqini.get_section\(aq, [path_to_ini_file, section_name]) .ft P .fi .UNINDENT @@ -197198,9 +197444,52 @@ salt \(aq*\(aq ini.get_section /path/to/ini section_name .UNINDENT .INDENT 0.0 .TP -.B salt.modules.ini_manage.remove_option(file_name, section, option, separator=\(aq=\(aq) +.B salt.modules.ini_manage.remove_option(file_name, section, option, separator=\(aq=\(aq, encoding=None) Remove a key/value pair from a section in an ini file. Returns the value of the removed key, or \fBNone\fP if nothing was removed. +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBfile_name\fP (\fI\%str\fP) \-\- The full path to the ini file. +.IP \(bu 2 +\fBsection\fP (\fI\%str\fP) \-\- A string value representing the section of the ini that the option +is in. If the option is not in a section, leave this empty. +.IP \(bu 2 +\fBoption\fP (\fI\%str\fP) \-\- A string value representing the option to search for. +.IP \(bu 2 +\fBseparator\fP (\fI\%str\fP) \-\- +.sp +The character used to separate keys and values. Standard ini files +use the \(dq=\(dq character. The default is \fB=\fP\&. +.sp +New in version 2016.11.0. + + +.IP \(bu 2 +\fBencoding\fP (\fI\%str\fP) \-\- +.sp +A string value representing encoding of the target ini file. If +\fBNone\fP is passed, it uses the system default which is likely +\fButf\-8\fP\&. Default is \fBNone\fP +.sp +New in version 3006.6. + + +.UNINDENT +.TP +.B Returns +.INDENT 7.0 +.TP +.B A string value representing the option that was removed or \fBNone\fP +if nothing was removed +.UNINDENT + +.TP +.B Return type +\fI\%str\fP +.UNINDENT .sp API Example: .INDENT 7.0 @@ -197210,8 +197499,7 @@ API Example: .ft C import salt sc = salt.client.get_local_client() -sc.cmd(\(aqtarget\(aq, \(aqini.remove_option\(aq, - [path_to_ini_file, section_name, option]) +sc.cmd(\(aqtarget\(aq, \(aqini.remove_option\(aq, [path_to_ini_file, section_name, option]) .ft P .fi .UNINDENT @@ -197231,9 +197519,49 @@ salt \(aq*\(aq ini.remove_option /path/to/ini section_name option_name .UNINDENT .INDENT 0.0 .TP -.B salt.modules.ini_manage.remove_section(file_name, section, separator=\(aq=\(aq) -Remove a section in an ini file. Returns the removed section as dictionary, -or \fBNone\fP if nothing was removed. +.B salt.modules.ini_manage.remove_section(file_name, section, separator=\(aq=\(aq, encoding=None) +Remove a section in an ini file. Returns the removed section as a +dictionary, or \fBNone\fP if nothing is removed. +.INDENT 7.0 +.TP +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBfile_name\fP (\fI\%str\fP) \-\- The full path to the ini file. +.IP \(bu 2 +\fBsection\fP (\fI\%str\fP) \-\- A string value representing the name of the section search for. +.IP \(bu 2 +\fBseparator\fP (\fI\%str\fP) \-\- +.sp +The character used to separate keys and values. Standard ini files +use the \(dq=\(dq character. The default is \fB=\fP\&. +.sp +New in version 2016.11.0. + + +.IP \(bu 2 +\fBencoding\fP (\fI\%str\fP) \-\- +.sp +A string value representing encoding of the target ini file. If +\fBNone\fP is passed, it uses the system default which is likely +\fButf\-8\fP\&. Default is \fBNone\fP +.sp +New in version 3006.6. + + +.UNINDENT +.TP +.B Returns +.INDENT 7.0 +.TP +.B A dictionary containing the names and values of all items in the +section that was removed or \fBNone\fP if nothing was removed +.UNINDENT + +.TP +.B Return type +\fI\%dict\fP +.UNINDENT .sp API Example: .INDENT 7.0 @@ -197243,8 +197571,7 @@ API Example: .ft C import salt.client with salt.client.get_local_client() as sc: - sc.cmd(\(aqtarget\(aq, \(aqini.remove_section\(aq, - [path_to_ini_file, section_name]) + sc.cmd(\(aqtarget\(aq, \(aqini.remove_section\(aq, [path_to_ini_file, section_name]) .ft P .fi .UNINDENT @@ -197264,29 +197591,46 @@ salt \(aq*\(aq ini.remove_section /path/to/ini section_name .UNINDENT .INDENT 0.0 .TP -.B salt.modules.ini_manage.set_option(file_name, sections=None, separator=\(aq=\(aq) +.B salt.modules.ini_manage.set_option(file_name, sections=None, separator=\(aq=\(aq, encoding=None) Edit an ini file, replacing one or more sections. Returns a dictionary containing the changes made. .INDENT 7.0 .TP -.B file_name -path of ini_file -.TP -.B sections -None -A dictionary representing the sections to be edited ini file -The keys are the section names and the values are the dictionary -containing the options -If the ini file does not contain sections the keys and values represent -the options -.TP -.B separator -= -A character used to separate keys and values. Standard ini files use -the \(dq=\(dq character. +.B Parameters +.INDENT 7.0 +.IP \(bu 2 +\fBfile_name\fP (\fI\%str\fP) \-\- The full path to the ini file. +.IP \(bu 2 +\fBsections\fP (\fI\%dict\fP) \-\- A dictionary representing the sections to be edited in the ini file. +The keys are the section names and the values are a dictionary +containing the options. If the ini file does not contain sections +the keys and values represent the options. The default is \fBNone\fP\&. +.IP \(bu 2 +\fBseparator\fP (\fI\%str\fP) \-\- +.sp +The character used to separate keys and values. Standard ini files +use the \(dq=\(dq character. The default is \fB=\fP\&. .sp New in version 2016.11.0. + +.IP \(bu 2 +\fBencoding\fP (\fI\%str\fP) \-\- +.sp +A string value representing encoding of the target ini file. If +\fBNone\fP is passed, it uses the system default which is likely +\fButf\-8\fP\&. Default is \fBNone\fP +.sp +New in version 3006.6. + + +.UNINDENT +.TP +.B Returns +A dictionary representing the changes made to the ini file +.TP +.B Return type +\fI\%dict\fP .UNINDENT .sp API Example: @@ -197298,8 +197642,7 @@ API Example: import salt.client with salt.client.get_local_client() as sc: sc.cmd( - \(aqtarget\(aq, \(aqini.set_option\(aq, - [\(aqpath_to_ini_file\(aq, \(aq{\(dqsection_to_change\(dq: {\(dqkey\(dq: \(dqvalue\(dq}}\(aq] + \(aqtarget\(aq, \(aqini.set_option\(aq, [\(aqpath_to_ini_file\(aq, \(aq{\(dqsection_to_change\(dq: {\(dqkey\(dq: \(dqvalue\(dq}}\(aq] ) .ft P .fi @@ -218197,7 +218540,7 @@ salt \(aq*\(aq assistive.enabled com.smileonmymac.textexpander .UNINDENT .INDENT 0.0 .TP -.B salt.modules.mac_assistive.install(app_id, enable=True) +.B salt.modules.mac_assistive.install(app_id, enable=True, tries=3, wait=10) Install a bundle ID or command as being allowed to use assistive access. .INDENT 7.0 @@ -218207,6 +218550,12 @@ The bundle ID or command to install for assistive access. .TP .B enabled Sets enabled or disabled status. Default is \fBTrue\fP\&. +.TP +.B tries +How many times to try and write to a read\-only tcc. Default is \fBTrue\fP\&. +.TP +.B wait +Number of seconds to wait between tries. Default is \fB10\fP\&. .UNINDENT .sp CLI Example: @@ -219107,7 +219456,7 @@ salt \(aq*\(aq keychain.get_default_keychain .UNINDENT .INDENT 0.0 .TP -.B salt.modules.mac_keychain.get_friendly_name(cert, password) +.B salt.modules.mac_keychain.get_friendly_name(cert, password, legacy=False) Get the friendly name of the given certificate .INDENT 7.0 .TP @@ -219120,6 +219469,9 @@ described for openssl command in the PASS PHRASE ARGUMENTS section .sp Note: The password given here will show up as plaintext in the returned job info. +.TP +.B legacy +Assume legacy format for certificate. .UNINDENT .sp CLI Example: @@ -219129,6 +219481,8 @@ CLI Example: .nf .ft C salt \(aq*\(aq keychain.get_friendly_name /tmp/test.p12 test123 + +salt \(aq*\(aq keychain.get_friendly_name /tmp/test.p12 test123 legacy=True .ft P .fi .UNINDENT @@ -220993,7 +221347,8 @@ Get the date/time the account was created \fBname\fP (\fI\%str\fP) \-\- The username of the account .TP .B Returns -The date/time the account was created (yyyy\-mm\-dd hh:mm:ss) +The date/time the account was created (yyyy\-mm\-dd hh:mm:ss) or 0 if +the value is not defined .TP .B Return type \fI\%str\fP @@ -221086,7 +221441,8 @@ Get the date/time the account was changed \fBname\fP (\fI\%str\fP) \-\- The username of the account .TP .B Returns -The date/time the account was modified (yyyy\-mm\-dd hh:mm:ss) +The date/time the account was modified (yyyy\-mm\-dd hh:mm:ss) or 0 +if the value is not defined .TP .B Return type \fI\%str\fP @@ -221117,10 +221473,11 @@ Get the number of failed login attempts \fBname\fP (\fI\%str\fP) \-\- The username of the account .TP .B Returns -The number of failed login attempts +The number of failed login attempts. 0 may mean there are no failed +login attempts or the value is not defined .TP .B Return type -\fI\%int\fP +\fI\%str\fP .TP .B Raises CommandExecutionError on user not found or any other unknown error @@ -221149,7 +221506,7 @@ Get the date/time of the last failed login attempt .TP .B Returns The date/time of the last failed login attempt on this account -(yyyy\-mm\-dd hh:mm:ss) +(yyyy\-mm\-dd hh:mm:ss) or 0 if the value is not defined .TP .B Return type \fI\%str\fP @@ -291579,7 +291936,8 @@ It exists as a more intuitive way of applying states. .sp APPLYING ALL STATES CONFIGURED IN TOP.SLS (A.K.A. \fI\%HIGHSTATE\fP) .sp -To apply all configured states, simply run \fBstate.apply\fP: +To apply all configured states, simply run \fBstate.apply\fP with no SLS +targets, like so: .INDENT 7.0 .INDENT 3.5 .sp @@ -302346,7 +302704,7 @@ New in version 3004. A transactional system, like \fI\%MicroOS\fP, can present some challenges when the user decided to manage it via Salt. .sp -MicroOS provide a read\-only rootfs and a tool, +MicroOS provides a read\-only rootfs and a tool, \fBtransactional\-update\fP, that takes care of the management of the system (updating, upgrading, installation or reboot, among others) in an atomic way. @@ -302473,7 +302831,7 @@ For example: .sp .nf .ft C -transactional\-update \-\-continue \-\-drop\-if\-no\-change run zypper in apache2\(dq +transactional\-update \-\-continue \-\-drop\-if\-no\-change run zypper in apache2 .ft P .fi .UNINDENT @@ -302591,11 +302949,11 @@ everything can be expressed inside a transaction (for example, restarting a service inside transaction is not allowed). .SS Two step for service restart .sp -In the \fBapache2\(ga example from the beginning we can observe the -biggest drawback. If the package \(ga\(gaapache2\fP is missing, the new +In the \fBapache2\fP example from the beginning we can observe the +biggest drawback. If the package \fBapache2\fP is missing, the new module will create a new transaction, will execute \fBpkg.install\fP inside the transaction (creating the salt\-thin, moving it inside and -delegating the execution to \fItransactional\-update\fP CLI as part of the +delegating the execution to \fBtransactional\-update\fP CLI as part of the full state). Inside the transaction we can do too the required changes in \fB/etc\fP for adding the new \fBvhost\fP, and we can enable the service via systemctl inside the same transaction. @@ -302645,7 +303003,7 @@ this function, check \fIstate.apply_\fP documentation. .TP .B activate_transaction If at the end of the transaction there is a pending activation -(i.e there is a new snaphot in the system), a new reboot will +(i.e there is a new snapshot in the system), a new reboot will be scheduled (default False) .UNINDENT .sp @@ -302707,7 +303065,7 @@ Salt execution module function .TP .B activate_transaction If at the end of the transaction there is a pending activation -(i.e there is a new snaphot in the system), a new reboot will +(i.e there is a new snapshot in the system), a new reboot will be scheduled (default False) .UNINDENT .sp @@ -302877,15 +303235,15 @@ this function, check \fIstate.highstate\fP documentation. .TP .B activate_transaction If at the end of the transaction there is a pending activation -(i.e there is a new snaphot in the system), a new reboot will -be scheduled (default False) +(i.e there is a new snapshot in the system), a new reboot will +be scheduled (Default: False). .TP .B queue Instead of failing immediately when another state run is in progress, queue the new state run to begin running once the other has finished. .sp This option starts a new thread for each queued state run, so use this -option sparingly. (Default: False) +option sparingly (Default: False). .UNINDENT .sp CLI Example: @@ -303245,8 +303603,8 @@ salt microos transactional_update rollback .B salt.modules.transactional_update.run(command, self_update=False, snapshot=None) Run a command in a new snapshot .sp -Execute the command inside a new snapshot. By default this snaphot -will remain, but if \-\-drop\-if\-no\-chage is set, the new snapshot +Execute the command inside a new snapshot. By default this snapshot +will remain, but if \-\-drop\-if\-no\-change is set, the new snapshot will be dropped if there is no change in the file system. .INDENT 7.0 .TP @@ -303289,15 +303647,15 @@ of keyword values is also supported. .TP .B activate_transaction If at the end of the transaction there is a pending activation -(i.e there is a new snaphot in the system), a new reboot will -be scheduled (default False) +(i.e there is a new snapshot in the system), a new reboot will +be scheduled (Default: False). .TP .B queue Instead of failing immediately when another state run is in progress, queue the new state run to begin running once the other has finished. .sp This option starts a new thread for each queued state run, so use this -option sparingly. (Default: False) +option sparingly (Default: False). .UNINDENT .sp CLI Example: @@ -303337,15 +303695,15 @@ may be used to match multiple states. .TP .B activate_transaction If at the end of the transaction there is a pending activation -(i.e there is a new snaphot in the system), a new reboot will -be scheduled (default False) +(i.e there is a new snapshot in the system), a new reboot will +be scheduled (Default: False). .TP .B queue Instead of failing immediately when another state run is in progress, queue the new state run to begin running once the other has finished. .sp This option starts a new thread for each queued state run, so use this -option sparingly. (Default: False) +option sparingly (Default: False). .UNINDENT .sp For a formal description of the possible parameters accepted in @@ -351598,6 +351956,27 @@ More info here: \fI\%https://docs.aws.amazon.com/cli/latest/topic/s3\-config.html\fP .UNINDENT .UNINDENT +.sp +\fBNOTE:\fP +.INDENT 0.0 +.INDENT 3.5 +This fileserver back\-end will by default sync all buckets on every +fileserver update. +.sp +If you want files to be only populated in the cache when requested, you can +disable this in the master config: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +s3.s3_sync_on_update: False +.ft P +.fi +.UNINDENT +.UNINDENT +.UNINDENT +.UNINDENT .SS salt.fileserver.svnfs .sp Subversion Fileserver Backend @@ -442311,7 +442690,7 @@ This is the top level of the registry. They all begin with HKEY. .IP \(bu 2 HKEY_CLASSES_ROOT (HKCR) .IP \(bu 2 -HKEY_CURRENT_USER(HKCU) +HKEY_CURRENT_USER (HKCU) .IP \(bu 2 HKEY_LOCAL MACHINE (HKLM) .IP \(bu 2 @@ -442393,7 +442772,7 @@ Ensure a registry value is removed. To remove a key use key_absent. A string value representing the full path of the key to include the HIVE, Key, and all Subkeys. For example: .sp -\fBHKEY_LOCAL_MACHINE\e\eSOFTWARE\e\eSalt\fP +\fBHKEY_LOCAL_MACHINE\eSOFTWARE\eSalt\fP .sp Valid hive values include: .INDENT 2.0 @@ -442433,7 +442812,7 @@ CLI Example: .sp .nf .ft C -\(aqHKEY_CURRENT_USER\e\eSOFTWARE\e\eSalt\(aq: +\(aqHKEY_CURRENT_USER\eSOFTWARE\eSalt\(aq: reg.absent \- vname: version .ft P @@ -442536,7 +442915,7 @@ Ensure a registry key or value is present. A string value representing the full path of the key to include the HIVE, Key, and all Subkeys. For example: .sp -\fBHKEY_LOCAL_MACHINE\e\eSOFTWARE\e\eSalt\fP +\fBHKEY_LOCAL_MACHINE\eSOFTWARE\eSalt\fP .sp Valid hive values include: .INDENT 2.0 @@ -442820,14 +443199,14 @@ A dictionary showing the results of the registry operation. Example: .sp The following example will set the \fB(Default)\fP value for the -\fBSOFTWARE\e\eSalt\fP key in the \fBHKEY_CURRENT_USER\fP hive to +\fBSOFTWARE\eSalt\fP key in the \fBHKEY_CURRENT_USER\fP hive to \fB2016.3.1\fP: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C -HKEY_CURRENT_USER\e\eSOFTWARE\e\eSalt: +HKEY_CURRENT_USER\eSOFTWARE\eSalt: reg.present: \- vdata: 2016.3.1 .ft P @@ -442838,14 +443217,14 @@ HKEY_CURRENT_USER\e\eSOFTWARE\e\eSalt: Example: .sp The following example will set the value for the \fBversion\fP entry under -the \fBSOFTWARE\e\eSalt\fP key in the \fBHKEY_CURRENT_USER\fP hive to +the \fBSOFTWARE\eSalt\fP key in the \fBHKEY_CURRENT_USER\fP hive to \fB2016.3.1\fP\&. The value will be reflected in \fBWow6432Node\fP: .INDENT 7.0 .INDENT 3.5 .sp .nf .ft C -HKEY_CURRENT_USER\e\eSOFTWARE\e\eSalt: +HKEY_CURRENT_USER\eSOFTWARE\eSalt: reg.present: \- vname: version \- vdata: 2016.3.1 @@ -442861,7 +443240,7 @@ In the above example the path is interpreted as follows: .IP \(bu 2 \fBHKEY_CURRENT_USER\fP is the hive .IP \(bu 2 -\fBSOFTWARE\e\eSalt\fP is the key +\fBSOFTWARE\eSalt\fP is the key .IP \(bu 2 \fBvname\fP is the value name (\(aqversion\(aq) that will be created under the key .IP \(bu 2 @@ -457898,7 +458277,7 @@ installed2 .UNINDENT .INDENT 0.0 .TP -.B salt.states.zcbuildout.installed(name, config=\(aqbuildout.cfg\(aq, quiet=False, parts=None, user=None, env=(), buildout_ver=None, test_release=False, distribute=None, new_st=None, offline=False, newest=False, python=\(aq/opt/actions\-runner/_work/salt\-priv/salt\-priv/.tools\-venvs/py3.10/docs/bin/python\(aq, debug=False, verbose=False, unless=None, onlyif=None, use_vt=False, loglevel=\(aqdebug\(aq, **kwargs) +.B salt.states.zcbuildout.installed(name, config=\(aqbuildout.cfg\(aq, quiet=False, parts=None, user=None, env=(), buildout_ver=None, test_release=False, distribute=None, new_st=None, offline=False, newest=False, python=\(aq/opt/actions\-runner/_work/salt/salt/.tools\-venvs/py3.10/docs/bin/python\(aq, debug=False, verbose=False, unless=None, onlyif=None, use_vt=False, loglevel=\(aqdebug\(aq, **kwargs) Install buildout in a specific directory .sp It is a thin wrapper to modules.buildout.buildout @@ -466917,7 +467296,7 @@ version of Python: .sp .nf .ft C -pyenv install 3.7.0 +pyenv install 3.9.18 .ft P .fi .UNINDENT @@ -466934,7 +467313,7 @@ new virtual environment with this command: .sp .nf .ft C -pyenv virtualenv 3.7.0 salt +pyenv virtualenv 3.9.18 salt .ft P .fi .UNINDENT @@ -467208,8 +467587,8 @@ you\(aqll need to run 3.9 or earlier. For example: .sp .nf .ft C -pyenv install 3.7.15 -pyenv virtualenv 3.7.15 salt\-docs +pyenv install 3.9.18 +pyenv virtualenv 3.9.18 salt\-docs echo \(aqsalt\-docs\(aq > .python\-version .ft P .fi @@ -467448,7 +467827,7 @@ meaningful and complete! \fITypically\fP the best tests for Salt are going to be unit tests. Testing is \fI\%a whole topic on its own\fP, But you may also want to write functional or integration tests. You\(aqll -find those in the \fBsalt/tests\fP directory. +find those in the \fBtests/\fP directory. .sp When you\(aqre thinking about tests to write, the most important thing to keep in mind is, “What, exactly, am I testing?” When a test fails, you @@ -478009,6 +478388,85 @@ Bump to \fBgitpython==3.1.41\fP due to \fI\%https://github.com/advisories/GHSA\- Bump to \fBjinja2==3.1.3\fP due to \fI\%https://github.com/advisories/GHSA\-h5c8\-rqwp\-cp95\fP \fI\%#65830\fP .UNINDENT .UNINDENT +(release\-3006.7)= +.SS Salt 3006.7 release notes +.SS Changelog +.SS Deprecated +.INDENT 0.0 +.IP \(bu 2 +Deprecate and stop using \fBsalt.features\fP \fI\%#65951\fP +.UNINDENT +.SS Changed +.INDENT 0.0 +.IP \(bu 2 +Change module search path priority, so Salt extensions can be overridden by syncable modules and module_dirs. You can switch back to the old logic by setting features.enable_deprecated_module_search_path_priority to true, but it will be removed in Salt 3008. \fI\%#65938\fP +.UNINDENT +.SS Fixed +.INDENT 0.0 +.IP \(bu 2 +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. \fI\%#34658\fP +.IP \(bu 2 +Fixed an issue when keys didn\(aqt match because of line endings \fI\%#52289\fP +.IP \(bu 2 +Corrected encoding of credentials for use with Artifactory \fI\%#63063\fP +.IP \(bu 2 +Use \fBsend_multipart\fP instead of \fBsend\fP when sending multipart message. \fI\%#65018\fP +.IP \(bu 2 +Fix an issue where the minion would crash on Windows if some of the grains +failed to resolve \fI\%#65154\fP +.IP \(bu 2 +Fix issue with openscap when the error was outside the expected scope. It now +returns failed with the error code and the error \fI\%#65193\fP +.IP \(bu 2 +Upgrade relenv to 0.15.0 to fix namespaced packages installed by salt\-pip \fI\%#65433\fP +.IP \(bu 2 +Fix regression of fileclient re\-use when rendering sls pillars and states \fI\%#65450\fP +.IP \(bu 2 +Fixes the s3fs backend computing the local cache\(aqs files with the wrong hash type \fI\%#65589\fP +.IP \(bu 2 +Fixed Salt\-SSH pillar rendering and state rendering with nested SSH calls when called via saltutil.cmd or in an orchestration \fI\%#65670\fP +.IP \(bu 2 +Fix boto execution module loading \fI\%#65691\fP +.IP \(bu 2 +Removed PR 65185 changes since incomplete solution \fI\%#65692\fP +.IP \(bu 2 +catch only ret/ events not all returning events. \fI\%#65727\fP +.IP \(bu 2 +Fix nonsensical time in fileclient timeout error. \fI\%#65752\fP +.IP \(bu 2 +Fixes an issue when reading/modifying ini files that contain unicode characters \fI\%#65777\fP +.IP \(bu 2 +added https proxy to the list of proxies so that requests knows what to do with https based proxies \fI\%#65824\fP +.IP \(bu 2 +Ensure minion channels are closed on any master connection error. \fI\%#65932\fP +.IP \(bu 2 +Fixed issue where Salt can\(aqt find libcrypto when pip installed from a cloned repo \fI\%#65954\fP +.IP \(bu 2 +Fix RPM package systemd scriptlets to make RPM packages more universal \fI\%#65987\fP +.IP \(bu 2 +Fixed an issue where fileclient requests during Pillar rendering cause +fileserver backends to be needlessly refreshed. \fI\%#65990\fP +.IP \(bu 2 +Fix exceptions being set on futures that are already done in ZeroMQ transport \fI\%#66006\fP +.IP \(bu 2 +Use hmac compare_digest method in hashutil module to mitigate potential timing attacks \fI\%#66041\fP +.IP \(bu 2 +Fix request channel default timeout regression. In 3006.5 it was changed from +60 to 30 and is now set back to 60 by default. \fI\%#66061\fP +.IP \(bu 2 +Upgrade relenv to 0.15.1 to fix debugpy support. \fI\%#66094\fP +.UNINDENT +.SS Security +.INDENT 0.0 +.IP \(bu 2 +Bump to \fBcryptography==42.0.0\fP due to \fI\%https://github.com/advisories/GHSA\-3ww4\-gg4f\-jr7f\fP +.sp +In the process, we were also required to update to \fBpyOpenSSL==24.0.0\fP \fI\%#66004\fP +.IP \(bu 2 +Bump to \fBcryptography==42.0.3\fP due to \fI\%https://github.com/advisories/GHSA\-3ww4\-gg4f\-jr7f\fP \fI\%#66090\fP +.UNINDENT .sp See \fI\%Install a release candidate\fP for more information about installing an RC when one is available. @@ -478998,6 +479456,19 @@ Bump to \fIurllib3==1.26.17\fP or \fIurllib3==2.0.6\fP due to \fI\%https://githu .IP \(bu 2 Bump to \fIgitpython==3.1.37\fP due to \fI\%https://github.com/advisories/GHSA\-cwvm\-v4w8\-q58c\fP (#65383) .UNINDENT +.SS Salt 3005.5 Release Notes +.sp +Version 3005.5 is a CVE security fix release for \fI\%3005\fP\&. +.SS Security +.INDENT 0.0 +.IP \(bu 2 +Fix CVE\-2024\-22231 by preventing directory traversal when creating syndic cache directory on the master. +.IP \(bu 2 +Fix CVE\-2024\-22232 Prevent directory traversal attacks in the master\(aqs serve_file method. +.UNINDENT +.sp +These vulnerablities were discovered and reported by: +Yudi Zhao(Huawei Nebula Security Lab),Chenwei Jiang(Huawei Nebula Security Lab) (#565) .SS Salt 3004 Release Notes \- Codename Silicon .SS New Features .SS Transactional System Support (MicroOS) diff --git a/doc/man/spm.1 b/doc/man/spm.1 index 1930413db07..f9be92b6be5 100644 --- a/doc/man/spm.1 +++ b/doc/man/spm.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] .in \\n[rst2man-indent\\n[rst2man-indent-level]]u .. -.TH "SPM" "1" "Generated on January 26, 2024 at 11:57:28 AM UTC." "3006.6" "Salt" +.TH "SPM" "1" "Generated on February 20, 2024 at 09:55:17 PM UTC." "3006.7" "Salt" .SH NAME spm \- Salt Package Manager Command .sp diff --git a/doc/ref/configuration/master.rst b/doc/ref/configuration/master.rst index f4d70eaf837..7c8ad3e8a07 100644 --- a/doc/ref/configuration/master.rst +++ b/doc/ref/configuration/master.rst @@ -112,8 +112,13 @@ Tell the master to also use salt-ssh when running commands against minions. .. note:: - Cross-minion communication is still not possible. The Salt mine and - publish.publish do not work between minion types. + Enabling this does not influence the limitations on cross-minion communication. + The Salt mine and ``publish.publish`` do not work from regular minions + to SSH minions, the other way around is partly possible since 3007.0 + (during state rendering on the master). + This means you can use the mentioned functions to call out to regular minions + in ``sls`` templates and wrapper modules, but state modules + (which are executed on the remote) relying on them still do not work. ``ret_port`` ------------ diff --git a/doc/ref/modules/all/index.rst b/doc/ref/modules/all/index.rst index b9a9c6fddad..800135e50d5 100644 --- a/doc/ref/modules/all/index.rst +++ b/doc/ref/modules/all/index.rst @@ -89,7 +89,6 @@ execution modules cabal capirca_acl cassandra_cql - cassandra_mod celery ceph chassis diff --git a/doc/ref/modules/all/salt.modules.cassandra_mod.rst b/doc/ref/modules/all/salt.modules.cassandra_mod.rst deleted file mode 100644 index 68f64c0e313..00000000000 --- a/doc/ref/modules/all/salt.modules.cassandra_mod.rst +++ /dev/null @@ -1,5 +0,0 @@ -salt.modules.cassandra_mod -========================== - -.. automodule:: salt.modules.cassandra_mod - :members: diff --git a/doc/ref/returners/all/index.rst b/doc/ref/returners/all/index.rst index 54e4555544d..edce578d4b8 100644 --- a/doc/ref/returners/all/index.rst +++ b/doc/ref/returners/all/index.rst @@ -13,10 +13,8 @@ returner modules appoptics_return carbon_return cassandra_cql_return - cassandra_return couchbase_return couchdb_return - django_return elasticsearch_return etcd_return highstate_return diff --git a/doc/ref/returners/all/salt.returners.cassandra_return.rst b/doc/ref/returners/all/salt.returners.cassandra_return.rst deleted file mode 100644 index a67ef5f0eff..00000000000 --- a/doc/ref/returners/all/salt.returners.cassandra_return.rst +++ /dev/null @@ -1,5 +0,0 @@ -salt.returners.cassandra_return -=============================== - -.. automodule:: salt.returners.cassandra_return - :members: diff --git a/doc/ref/returners/all/salt.returners.django_return.rst b/doc/ref/returners/all/salt.returners.django_return.rst deleted file mode 100644 index b63df7ff19d..00000000000 --- a/doc/ref/returners/all/salt.returners.django_return.rst +++ /dev/null @@ -1,5 +0,0 @@ -salt.returners.django_return -============================ - -.. automodule:: salt.returners.django_return - :members: diff --git a/doc/ref/states/all/index.rst b/doc/ref/states/all/index.rst index f9f67257bb6..924979985fa 100644 --- a/doc/ref/states/all/index.rst +++ b/doc/ref/states/all/index.rst @@ -339,6 +339,7 @@ state modules win_smtp_server win_snmp win_system + win_task win_wua win_wusa winrepo diff --git a/doc/ref/states/all/salt.states.win_task.rst b/doc/ref/states/all/salt.states.win_task.rst new file mode 100644 index 00000000000..c675c2caba7 --- /dev/null +++ b/doc/ref/states/all/salt.states.win_task.rst @@ -0,0 +1,5 @@ +salt.states.win_task +==================== + +.. automodule:: salt.states.win_task + :members: diff --git a/doc/topics/development/modules/index.rst b/doc/topics/development/modules/index.rst index 602a45f6cb3..fb1408e7dbd 100644 --- a/doc/topics/development/modules/index.rst +++ b/doc/topics/development/modules/index.rst @@ -132,7 +132,7 @@ Execution ``salt.modules`` (:ref:`index `) `` Executor ``salt.executors`` (:ref:`index `) ``executors`` ``executor_dirs`` File Server ``salt.fileserver`` (:ref:`index `) ``fileserver`` ``fileserver_dirs`` Grain ``salt.grains`` (:ref:`index `) ``grains`` ``grains_dirs`` -Log Handler ``salt.log.handlers`` (:ref:`index `) ``log_handlers`` ``log_handlers_dirs`` +Log Handler ``salt.log_handlers`` (:ref:`index `) ``log_handlers`` ``log_handlers_dirs`` Matcher ``salt.matchers`` ``matchers`` ``matchers_dirs`` Metaproxy ``salt.metaproxy`` ``metaproxy`` [#no-fs]_ ``metaproxy_dirs`` Net API ``salt.netapi`` (:ref:`index `) ``netapi`` [#no-fs]_ ``netapi_dirs`` diff --git a/doc/topics/releases/3006.7.md b/doc/topics/releases/3006.7.md new file mode 100644 index 00000000000..d9776b05eea --- /dev/null +++ b/doc/topics/releases/3006.7.md @@ -0,0 +1,70 @@ +(release-3006.7)= +# Salt 3006.7 release notes + + + + + + + +## Changelog + +### Deprecated + +- Deprecate and stop using ``salt.features`` [#65951](https://github.com/saltstack/salt/issues/65951) + + +### Changed + +- Change module search path priority, so Salt extensions can be overridden by syncable modules and module_dirs. You can switch back to the old logic by setting features.enable_deprecated_module_search_path_priority to true, but it will be removed in Salt 3008. [#65938](https://github.com/saltstack/salt/issues/65938) + + +### Fixed + +- 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) +- Fixed an issue when keys didn't match because of line endings [#52289](https://github.com/saltstack/salt/issues/52289) +- Corrected encoding of credentials for use with Artifactory [#63063](https://github.com/saltstack/salt/issues/63063) +- Use `send_multipart` instead of `send` when sending multipart message. [#65018](https://github.com/saltstack/salt/issues/65018) +- Fix an issue where the minion would crash on Windows if some of the grains + failed to resolve [#65154](https://github.com/saltstack/salt/issues/65154) +- Fix issue with openscap when the error was outside the expected scope. It now + returns failed with the error code and the error [#65193](https://github.com/saltstack/salt/issues/65193) +- Upgrade relenv to 0.15.0 to fix namespaced packages installed by salt-pip [#65433](https://github.com/saltstack/salt/issues/65433) +- Fix regression of fileclient re-use when rendering sls pillars and states [#65450](https://github.com/saltstack/salt/issues/65450) +- Fixes the s3fs backend computing the local cache's files with the wrong hash type [#65589](https://github.com/saltstack/salt/issues/65589) +- Fixed Salt-SSH pillar rendering and state rendering with nested SSH calls when called via saltutil.cmd or in an orchestration [#65670](https://github.com/saltstack/salt/issues/65670) +- Fix boto execution module loading [#65691](https://github.com/saltstack/salt/issues/65691) +- Removed PR 65185 changes since incomplete solution [#65692](https://github.com/saltstack/salt/issues/65692) +- catch only ret/ events not all returning events. [#65727](https://github.com/saltstack/salt/issues/65727) +- Fix nonsensical time in fileclient timeout error. [#65752](https://github.com/saltstack/salt/issues/65752) +- Fixes an issue when reading/modifying ini files that contain unicode characters [#65777](https://github.com/saltstack/salt/issues/65777) +- added https proxy to the list of proxies so that requests knows what to do with https based proxies [#65824](https://github.com/saltstack/salt/issues/65824) +- Ensure minion channels are closed on any master connection error. [#65932](https://github.com/saltstack/salt/issues/65932) +- Fixed issue where Salt can't find libcrypto when pip installed from a cloned repo [#65954](https://github.com/saltstack/salt/issues/65954) +- Fix RPM package systemd scriptlets to make RPM packages more universal [#65987](https://github.com/saltstack/salt/issues/65987) +- Fixed an issue where fileclient requests during Pillar rendering cause + fileserver backends to be needlessly refreshed. [#65990](https://github.com/saltstack/salt/issues/65990) +- Fix exceptions being set on futures that are already done in ZeroMQ transport [#66006](https://github.com/saltstack/salt/issues/66006) +- Use hmac compare_digest method in hashutil module to mitigate potential timing attacks [#66041](https://github.com/saltstack/salt/issues/66041) +- Fix request channel default timeout regression. In 3006.5 it was changed from + 60 to 30 and is now set back to 60 by default. [#66061](https://github.com/saltstack/salt/issues/66061) +- Upgrade relenv to 0.15.1 to fix debugpy support. [#66094](https://github.com/saltstack/salt/issues/66094) + + +### Security + +- Bump to ``cryptography==42.0.0`` due to https://github.com/advisories/GHSA-3ww4-gg4f-jr7f + + In the process, we were also required to update to ``pyOpenSSL==24.0.0`` [#66004](https://github.com/saltstack/salt/issues/66004) +- Bump to `cryptography==42.0.3` due to https://github.com/advisories/GHSA-3ww4-gg4f-jr7f [#66090](https://github.com/saltstack/salt/issues/66090) diff --git a/doc/topics/releases/templates/3006.7.md.template b/doc/topics/releases/templates/3006.7.md.template new file mode 100644 index 00000000000..3740af58042 --- /dev/null +++ b/doc/topics/releases/templates/3006.7.md.template @@ -0,0 +1,14 @@ +(release-3006.7)= +# Salt 3006.7 release notes{{ unreleased }} +{{ warning }} + + + + +## Changelog +{{ changelog }} diff --git a/pkg/debian/changelog b/pkg/debian/changelog index 839f533c20f..c615e70d78a 100644 --- a/pkg/debian/changelog +++ b/pkg/debian/changelog @@ -1,3 +1,57 @@ +salt (3006.7) stable; urgency=medium + + + # Deprecated + + * Deprecate and stop using ``salt.features`` [#65951](https://github.com/saltstack/salt/issues/65951) + + # Changed + + * Change module search path priority, so Salt extensions can be overridden by syncable modules and module_dirs. You can switch back to the old logic by setting features.enable_deprecated_module_search_path_priority to true, but it will be removed in Salt 3008. [#65938](https://github.com/saltstack/salt/issues/65938) + + # Fixed + + * 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) + * Fixed an issue when keys didn't match because of line endings [#52289](https://github.com/saltstack/salt/issues/52289) + * Corrected encoding of credentials for use with Artifactory [#63063](https://github.com/saltstack/salt/issues/63063) + * Use `send_multipart` instead of `send` when sending multipart message. [#65018](https://github.com/saltstack/salt/issues/65018) + * Fix an issue where the minion would crash on Windows if some of the grains + failed to resolve [#65154](https://github.com/saltstack/salt/issues/65154) + * Fix issue with openscap when the error was outside the expected scope. It now + returns failed with the error code and the error [#65193](https://github.com/saltstack/salt/issues/65193) + * Upgrade relenv to 0.15.0 to fix namespaced packages installed by salt-pip [#65433](https://github.com/saltstack/salt/issues/65433) + * Fix regression of fileclient re-use when rendering sls pillars and states [#65450](https://github.com/saltstack/salt/issues/65450) + * Fixes the s3fs backend computing the local cache's files with the wrong hash type [#65589](https://github.com/saltstack/salt/issues/65589) + * Fixed Salt-SSH pillar rendering and state rendering with nested SSH calls when called via saltutil.cmd or in an orchestration [#65670](https://github.com/saltstack/salt/issues/65670) + * Fix boto execution module loading [#65691](https://github.com/saltstack/salt/issues/65691) + * Removed PR 65185 changes since incomplete solution [#65692](https://github.com/saltstack/salt/issues/65692) + * catch only ret/ events not all returning events. [#65727](https://github.com/saltstack/salt/issues/65727) + * Fix nonsensical time in fileclient timeout error. [#65752](https://github.com/saltstack/salt/issues/65752) + * Fixes an issue when reading/modifying ini files that contain unicode characters [#65777](https://github.com/saltstack/salt/issues/65777) + * added https proxy to the list of proxies so that requests knows what to do with https based proxies [#65824](https://github.com/saltstack/salt/issues/65824) + * Ensure minion channels are closed on any master connection error. [#65932](https://github.com/saltstack/salt/issues/65932) + * Fixed issue where Salt can't find libcrypto when pip installed from a cloned repo [#65954](https://github.com/saltstack/salt/issues/65954) + * Fix RPM package systemd scriptlets to make RPM packages more universal [#65987](https://github.com/saltstack/salt/issues/65987) + * Fixed an issue where fileclient requests during Pillar rendering cause + fileserver backends to be needlessly refreshed. [#65990](https://github.com/saltstack/salt/issues/65990) + * Fix exceptions being set on futures that are already done in ZeroMQ transport [#66006](https://github.com/saltstack/salt/issues/66006) + * Use hmac compare_digest method in hashutil module to mitigate potential timing attacks [#66041](https://github.com/saltstack/salt/issues/66041) + * Fix request channel default timeout regression. In 3006.5 it was changed from + 60 to 30 and is now set back to 60 by default. [#66061](https://github.com/saltstack/salt/issues/66061) + * Upgrade relenv to 0.15.1 to fix debugpy support. [#66094](https://github.com/saltstack/salt/issues/66094) + + # Security + + * Bump to ``cryptography==42.0.0`` due to https://github.com/advisories/GHSA-3ww4-gg4f-jr7f + + In the process, we were also required to update to ``pyOpenSSL==24.0.0`` [#66004](https://github.com/saltstack/salt/issues/66004) + * Bump to `cryptography==42.0.3` due to https://github.com/advisories/GHSA-3ww4-gg4f-jr7f [#66090](https://github.com/saltstack/salt/issues/66090) + + + -- Salt Project Packaging Tue, 20 Feb 2024 21:54:35 +0000 + salt (3006.6) stable; urgency=medium diff --git a/pkg/rpm/salt.spec b/pkg/rpm/salt.spec index 96a331e323b..289c204ab24 100644 --- a/pkg/rpm/salt.spec +++ b/pkg/rpm/salt.spec @@ -432,17 +432,33 @@ find /etc/salt /opt/saltstack/salt /var/log/salt /var/cache/salt /var/run/salt \ # assumes systemd for RHEL 7 & 8 & 9 +# foregoing %systemd_* scriptlets due to RHEL 7/8 vs. RHEL 9 incompatibilities +## - Using hardcoded scriptlet definitions from RHEL 7/8 that are forward-compatible %preun master # RHEL 9 is giving warning msg if syndic is not installed, supress it -%systemd_preun salt-syndic.service > /dev/null 2>&1 - +# %%systemd_preun salt-syndic.service > /dev/null 2>&1 +if [ $1 -eq 0 ] ; then + # Package removal, not upgrade + systemctl --no-reload disable salt-syndic.service > /dev/null 2>&1 || : + systemctl stop salt-syndic.service > /dev/null 2>&1 || : +fi %preun minion -%systemd_preun salt-minion.service +# %%systemd_preun salt-minion.service +if [ $1 -eq 0 ] ; then + # Package removal, not upgrade + systemctl --no-reload disable salt-minion.service > /dev/null 2>&1 || : + systemctl stop salt-minion.service > /dev/null 2>&1 || : +fi %preun api -%systemd_preun salt-api.service +# %%systemd_preun salt-api.service +if [ $1 -eq 0 ] ; then + # Package removal, not upgrade + systemctl --no-reload disable salt-api.service > /dev/null 2>&1 || : + systemctl stop salt-api.service > /dev/null 2>&1 || : +fi %post @@ -456,7 +472,14 @@ ln -s -f /opt/saltstack/salt/salt-cloud %{_bindir}/salt-cloud %post master -%systemd_post salt-master.service +# %%systemd_post salt-master.service +if [ $1 -gt 1 ] ; then + # Upgrade + systemctl retry-restart salt-master.service >/dev/null 2>&1 || : +else + # Initial installation + systemctl preset salt-master.service >/dev/null 2>&1 || : +fi ln -s -f /opt/saltstack/salt/salt %{_bindir}/salt ln -s -f /opt/saltstack/salt/salt-cp %{_bindir}/salt-cp ln -s -f /opt/saltstack/salt/salt-key %{_bindir}/salt-key @@ -477,11 +500,25 @@ if [ $1 -lt 2 ]; then fi %post syndic -%systemd_post salt-syndic.service +# %%systemd_post salt-syndic.service +if [ $1 -gt 1 ] ; then + # Upgrade + systemctl retry-restart salt-syndic.service >/dev/null 2>&1 || : +else + # Initial installation + systemctl preset salt-syndic.service >/dev/null 2>&1 || : +fi ln -s -f /opt/saltstack/salt/salt-syndic %{_bindir}/salt-syndic %post minion -%systemd_post salt-minion.service +# %%systemd_post salt-minion.service +if [ $1 -gt 1 ] ; then + # Upgrade + systemctl retry-restart salt-minion.service >/dev/null 2>&1 || : +else + # Initial installation + systemctl preset salt-minion.service >/dev/null 2>&1 || : +fi ln -s -f /opt/saltstack/salt/salt-minion %{_bindir}/salt-minion ln -s -f /opt/saltstack/salt/salt-call %{_bindir}/salt-call ln -s -f /opt/saltstack/salt/salt-proxy %{_bindir}/salt-proxy @@ -503,7 +540,14 @@ fi ln -s -f /opt/saltstack/salt/salt-ssh %{_bindir}/salt-ssh %post api -%systemd_post salt-api.service +# %%systemd_post salt-api.service +if [ $1 -gt 1 ] ; then + # Upgrade + systemctl retry-restart salt-api.service >/dev/null 2>&1 || : +else + # Initial installation + systemctl preset salt-api.service >/dev/null 2>&1 || : +fi ln -s -f /opt/saltstack/salt/salt-api %{_bindir}/salt-api @@ -544,7 +588,12 @@ if [ $1 -eq 0 ]; then fi %postun master -%systemd_postun_with_restart salt-master.service +# %%systemd_postun_with_restart salt-master.service +systemctl daemon-reload >/dev/null 2>&1 || : +if [ $1 -ge 1 ] ; then + # Package upgrade, not uninstall + systemctl try-restart salt-master.service >/dev/null 2>&1 || : +fi if [ $1 -eq 0 ]; then if [ $(cat /etc/os-release | grep VERSION_ID | cut -d '=' -f 2 | sed 's/\"//g' | cut -d '.' -f 1) = "8" ]; then if [ -z "$(rpm -qi salt-minion | grep Name | grep salt-minion)" ]; then @@ -560,10 +609,20 @@ if [ $1 -eq 0 ]; then fi %postun syndic -%systemd_postun_with_restart salt-syndic.service +# %%systemd_postun_with_restart salt-syndic.service +systemctl daemon-reload >/dev/null 2>&1 || : +if [ $1 -ge 1 ] ; then + # Package upgrade, not uninstall + systemctl try-restart salt-syndic.service >/dev/null 2>&1 || : +fi %postun minion -%systemd_postun_with_restart salt-minion.service +# %%systemd_postun_with_restart salt-minion.service +systemctl daemon-reload >/dev/null 2>&1 || : +if [ $1 -ge 1 ] ; then + # Package upgrade, not uninstall + systemctl try-restart salt-minion.service >/dev/null 2>&1 || : +fi if [ $1 -eq 0 ]; then if [ $(cat /etc/os-release | grep VERSION_ID | cut -d '=' -f 2 | sed 's/\"//g' | cut -d '.' -f 1) = "8" ]; then if [ -z "$(rpm -qi salt-master | grep Name | grep salt-master)" ]; then @@ -579,10 +638,65 @@ if [ $1 -eq 0 ]; then fi %postun api -%systemd_postun_with_restart salt-api.service - +# %%systemd_postun_with_restart salt-api.service +systemctl daemon-reload >/dev/null 2>&1 || : +if [ $1 -ge 1 ] ; then + # Package upgrade, not uninstall + systemctl try-restart salt-api.service >/dev/null 2>&1 || : +fi %changelog +* Tue Feb 20 2024 Salt Project Packaging - 3006.7 + +# Deprecated + +- Deprecate and stop using ``salt.features`` [#65951](https://github.com/saltstack/salt/issues/65951) + +# Changed + +- Change module search path priority, so Salt extensions can be overridden by syncable modules and module_dirs. You can switch back to the old logic by setting features.enable_deprecated_module_search_path_priority to true, but it will be removed in Salt 3008. [#65938](https://github.com/saltstack/salt/issues/65938) + +# Fixed + +- 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) +- Fixed an issue when keys didn't match because of line endings [#52289](https://github.com/saltstack/salt/issues/52289) +- Corrected encoding of credentials for use with Artifactory [#63063](https://github.com/saltstack/salt/issues/63063) +- Use `send_multipart` instead of `send` when sending multipart message. [#65018](https://github.com/saltstack/salt/issues/65018) +- Fix an issue where the minion would crash on Windows if some of the grains + failed to resolve [#65154](https://github.com/saltstack/salt/issues/65154) +- Fix issue with openscap when the error was outside the expected scope. It now + returns failed with the error code and the error [#65193](https://github.com/saltstack/salt/issues/65193) +- Upgrade relenv to 0.15.0 to fix namespaced packages installed by salt-pip [#65433](https://github.com/saltstack/salt/issues/65433) +- Fix regression of fileclient re-use when rendering sls pillars and states [#65450](https://github.com/saltstack/salt/issues/65450) +- Fixes the s3fs backend computing the local cache's files with the wrong hash type [#65589](https://github.com/saltstack/salt/issues/65589) +- Fixed Salt-SSH pillar rendering and state rendering with nested SSH calls when called via saltutil.cmd or in an orchestration [#65670](https://github.com/saltstack/salt/issues/65670) +- Fix boto execution module loading [#65691](https://github.com/saltstack/salt/issues/65691) +- Removed PR 65185 changes since incomplete solution [#65692](https://github.com/saltstack/salt/issues/65692) +- catch only ret/ events not all returning events. [#65727](https://github.com/saltstack/salt/issues/65727) +- Fix nonsensical time in fileclient timeout error. [#65752](https://github.com/saltstack/salt/issues/65752) +- Fixes an issue when reading/modifying ini files that contain unicode characters [#65777](https://github.com/saltstack/salt/issues/65777) +- added https proxy to the list of proxies so that requests knows what to do with https based proxies [#65824](https://github.com/saltstack/salt/issues/65824) +- Ensure minion channels are closed on any master connection error. [#65932](https://github.com/saltstack/salt/issues/65932) +- Fixed issue where Salt can't find libcrypto when pip installed from a cloned repo [#65954](https://github.com/saltstack/salt/issues/65954) +- Fix RPM package systemd scriptlets to make RPM packages more universal [#65987](https://github.com/saltstack/salt/issues/65987) +- Fixed an issue where fileclient requests during Pillar rendering cause + fileserver backends to be needlessly refreshed. [#65990](https://github.com/saltstack/salt/issues/65990) +- Fix exceptions being set on futures that are already done in ZeroMQ transport [#66006](https://github.com/saltstack/salt/issues/66006) +- Use hmac compare_digest method in hashutil module to mitigate potential timing attacks [#66041](https://github.com/saltstack/salt/issues/66041) +- Fix request channel default timeout regression. In 3006.5 it was changed from + 60 to 30 and is now set back to 60 by default. [#66061](https://github.com/saltstack/salt/issues/66061) +- Upgrade relenv to 0.15.1 to fix debugpy support. [#66094](https://github.com/saltstack/salt/issues/66094) + +# Security + +- Bump to ``cryptography==42.0.0`` due to https://github.com/advisories/GHSA-3ww4-gg4f-jr7f + + In the process, we were also required to update to ``pyOpenSSL==24.0.0`` [#66004](https://github.com/saltstack/salt/issues/66004) +- Bump to `cryptography==42.0.3` due to https://github.com/advisories/GHSA-3ww4-gg4f-jr7f [#66090](https://github.com/saltstack/salt/issues/66090) + + * Fri Jan 26 2024 Salt Project Packaging - 3006.6 # Changed diff --git a/requirements/base.txt b/requirements/base.txt index ece8691d2ec..a69fef1dd1b 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -19,12 +19,12 @@ contextvars setproctitle>=1.2.3 timelib>=0.2.5 -pyopenssl>=20.0.1 +pyopenssl>=24.0.0 python-dateutil>=2.8.1 python-gnupg>=0.4.7 cherrypy>=18.6.1 importlib-metadata>=3.3.0 -cryptography>=41.0.3 +cryptography>=42.0.0 # From old requirements/static/pkg/linux.in rpm-vercmp; sys_platform == 'linux' diff --git a/requirements/static/ci/py3.10/cloud.txt b/requirements/static/ci/py3.10/cloud.txt index 74839863f8e..ca0cd5c5dc2 100644 --- a/requirements/static/ci/py3.10/cloud.txt +++ b/requirements/static/ci/py3.10/cloud.txt @@ -20,7 +20,7 @@ charset-normalizer==3.2.0 # via # -c requirements/static/ci/py3.10/linux.txt # requests -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/py3.10/linux.txt # pyspnego diff --git a/requirements/static/ci/py3.10/darwin.txt b/requirements/static/ci/py3.10/darwin.txt index ea221aa6f88..cab75542dbf 100644 --- a/requirements/static/ci/py3.10/darwin.txt +++ b/requirements/static/ci/py3.10/darwin.txt @@ -23,7 +23,7 @@ async-timeout==4.0.3 # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt # aiohttp -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt # aiohttp @@ -97,7 +97,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt # -r requirements/base.txt @@ -164,7 +164,7 @@ importlib-metadata==6.6.0 # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt # -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt # jaraco.text @@ -340,7 +340,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt # -r requirements/base.txt @@ -399,11 +399,11 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt # tempora @@ -422,7 +422,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt # -r requirements/zeromq.txt @@ -519,6 +519,7 @@ types-pyyaml==6.0.1 typing-extensions==4.8.0 # via # -c requirements/static/ci/../pkg/py3.10/darwin.txt + # inflect # napalm # pydantic # pytest-shell-utilities diff --git a/requirements/static/ci/py3.10/docs.txt b/requirements/static/ci/py3.10/docs.txt index 11cfa4e7a30..6ddfd8b7294 100644 --- a/requirements/static/ci/py3.10/docs.txt +++ b/requirements/static/ci/py3.10/docs.txt @@ -36,7 +36,7 @@ idna==3.4 # requests imagesize==1.4.1 # via sphinx -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/py3.10/linux.txt # jaraco.text @@ -103,7 +103,7 @@ pyenchant==3.2.2 # via sphinxcontrib-spelling pygments==2.15.1 # via sphinx -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/py3.10/linux.txt # tempora @@ -149,6 +149,7 @@ tempora==5.3.0 typing-extensions==4.8.0 # via # -c requirements/static/ci/py3.10/linux.txt + # inflect # pydantic uc-micro-py==1.0.2 # via linkify-it-py diff --git a/requirements/static/ci/py3.10/freebsd.txt b/requirements/static/ci/py3.10/freebsd.txt index ed2c485aee1..1a51e9234f5 100644 --- a/requirements/static/ci/py3.10/freebsd.txt +++ b/requirements/static/ci/py3.10/freebsd.txt @@ -23,7 +23,7 @@ async-timeout==4.0.3 # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt # aiohttp -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt # aiohttp @@ -96,7 +96,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt # -r requirements/base.txt @@ -163,7 +163,7 @@ importlib-metadata==6.6.0 # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt # -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt # jaraco.text @@ -344,7 +344,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt # -r requirements/base.txt @@ -403,11 +403,11 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt # tempora @@ -426,7 +426,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt # -r requirements/zeromq.txt @@ -524,6 +524,7 @@ types-pyyaml==6.0.1 typing-extensions==4.8.0 # via # -c requirements/static/ci/../pkg/py3.10/freebsd.txt + # inflect # napalm # pydantic # pytest-shell-utilities diff --git a/requirements/static/ci/py3.10/linux.txt b/requirements/static/ci/py3.10/linux.txt index d226e747097..82b238aface 100644 --- a/requirements/static/ci/py3.10/linux.txt +++ b/requirements/static/ci/py3.10/linux.txt @@ -17,7 +17,7 @@ aiosignal==1.3.1 # via # -c requirements/static/ci/../pkg/py3.10/linux.txt # aiohttp -ansible-core==2.16.2 +ansible-core==2.16.3 # via ansible ansible==9.1.0 ; python_version >= "3.10" # via -r requirements/static/ci/linux.in @@ -33,7 +33,7 @@ async-timeout==4.0.3 # via # -c requirements/static/ci/../pkg/py3.10/linux.txt # aiohttp -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.10/linux.txt # aiohttp @@ -109,7 +109,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.10/linux.txt # -r requirements/base.txt @@ -187,7 +187,7 @@ importlib-metadata==6.6.0 # via # -c requirements/static/ci/../pkg/py3.10/linux.txt # -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/../pkg/py3.10/linux.txt # jaraco.text @@ -378,7 +378,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.10/linux.txt # -r requirements/base.txt @@ -439,13 +439,13 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.10/linux.txt # -r requirements/base.txt python-telegram-bot==20.3 # via -r requirements/static/ci/linux.in -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.10/linux.txt # tempora @@ -466,7 +466,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.10/linux.txt # -r requirements/zeromq.txt @@ -588,6 +588,7 @@ types-pyyaml==6.0.1 typing-extensions==4.8.0 # via # -c requirements/static/ci/../pkg/py3.10/linux.txt + # inflect # napalm # pydantic # pytest-shell-utilities diff --git a/requirements/static/ci/py3.10/tools.txt b/requirements/static/ci/py3.10/tools.txt index 505a5d38221..3595e5a0106 100644 --- a/requirements/static/ci/py3.10/tools.txt +++ b/requirements/static/ci/py3.10/tools.txt @@ -4,7 +4,7 @@ # # pip-compile --no-emit-index-url --output-file=requirements/static/ci/py3.10/tools.txt requirements/static/ci/tools.in # -attrs==20.3.0 +attrs==23.2.0 # via # -r requirements/static/ci/tools.in # python-tools-scripts diff --git a/requirements/static/ci/py3.10/windows.txt b/requirements/static/ci/py3.10/windows.txt index 24284b0029b..98868aacb97 100644 --- a/requirements/static/ci/py3.10/windows.txt +++ b/requirements/static/ci/py3.10/windows.txt @@ -17,7 +17,7 @@ async-timeout==4.0.3 # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # aiohttp -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # aiohttp @@ -93,7 +93,7 @@ contextvars==2.4 # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # -r requirements/base.txt @@ -156,7 +156,7 @@ importlib-metadata==6.6.0 # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # jaraco.text @@ -301,7 +301,7 @@ pymysql==1.1.0 ; sys_platform == "win32" # -r requirements/base.txt pynacl==1.5.0 # via -r requirements/static/ci/common.in -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # -r requirements/base.txt @@ -354,15 +354,15 @@ python-dateutil==2.8.2 # moto python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # -r requirements/base.txt -pythonnet==3.0.1 ; sys_platform == "win32" +pythonnet==3.0.3 ; sys_platform == "win32" # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # tempora @@ -386,7 +386,7 @@ pyyaml==6.0.1 # pytest-salt-factories # responses # yamllint -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.10/windows.txt # -r requirements/zeromq.txt @@ -464,6 +464,7 @@ types-pyyaml==6.0.1 typing-extensions==4.8.0 # via # -c requirements/static/ci/../pkg/py3.10/windows.txt + # inflect # pydantic # pytest-shell-utilities # pytest-system-statistics diff --git a/requirements/static/ci/py3.11/cloud.txt b/requirements/static/ci/py3.11/cloud.txt index fe7d9462f56..8500afc4533 100644 --- a/requirements/static/ci/py3.11/cloud.txt +++ b/requirements/static/ci/py3.11/cloud.txt @@ -20,7 +20,7 @@ charset-normalizer==3.2.0 # via # -c requirements/static/ci/py3.11/linux.txt # requests -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/py3.11/linux.txt # pyspnego diff --git a/requirements/static/ci/py3.11/darwin.txt b/requirements/static/ci/py3.11/darwin.txt index 5876434b7ae..e8e9e1446ba 100644 --- a/requirements/static/ci/py3.11/darwin.txt +++ b/requirements/static/ci/py3.11/darwin.txt @@ -23,12 +23,11 @@ asn1crypto==1.5.1 # via # certvalidator # oscrypto -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.11/darwin.txt # aiohttp # jsonschema - # pytest # pytest-salt-factories # pytest-shell-utilities # pytest-skip-markers @@ -97,7 +96,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.11/darwin.txt # -r requirements/base.txt @@ -342,7 +341,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.11/darwin.txt # -r requirements/base.txt @@ -379,7 +378,7 @@ pytest-system-statistics==1.0.2 # via pytest-salt-factories pytest-timeout==1.4.2 # via -r requirements/pytest.txt -pytest==7.2.0 +pytest==7.3.2 # via # -r requirements/pytest.txt # pytest-custom-exit-code @@ -401,11 +400,11 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.11/darwin.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.11/darwin.txt # tempora @@ -424,7 +423,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.11/darwin.txt # -r requirements/zeromq.txt diff --git a/requirements/static/ci/py3.11/docs.txt b/requirements/static/ci/py3.11/docs.txt index d5d368ac7c8..2d4f7db8b12 100644 --- a/requirements/static/ci/py3.11/docs.txt +++ b/requirements/static/ci/py3.11/docs.txt @@ -111,7 +111,7 @@ pyenchant==3.2.2 # via sphinxcontrib-spelling pygments==2.15.1 # via sphinx -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/py3.11/linux.txt # tempora diff --git a/requirements/static/ci/py3.11/freebsd.txt b/requirements/static/ci/py3.11/freebsd.txt index 2ecaf8c7682..97687e80ba5 100644 --- a/requirements/static/ci/py3.11/freebsd.txt +++ b/requirements/static/ci/py3.11/freebsd.txt @@ -23,12 +23,11 @@ asn1crypto==1.5.1 # via # certvalidator # oscrypto -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.11/freebsd.txt # aiohttp # jsonschema - # pytest # pytest-salt-factories # pytest-shell-utilities # pytest-skip-markers @@ -96,7 +95,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.11/freebsd.txt # -r requirements/base.txt @@ -346,7 +345,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.11/freebsd.txt # -r requirements/base.txt @@ -383,7 +382,7 @@ pytest-system-statistics==1.0.2 # via pytest-salt-factories pytest-timeout==1.4.2 # via -r requirements/pytest.txt -pytest==7.2.0 +pytest==7.3.2 # via # -r requirements/pytest.txt # pytest-custom-exit-code @@ -405,11 +404,11 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.11/freebsd.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.11/freebsd.txt # tempora @@ -428,7 +427,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.11/freebsd.txt # -r requirements/zeromq.txt diff --git a/requirements/static/ci/py3.11/linux.txt b/requirements/static/ci/py3.11/linux.txt index eb4187cd968..b7813920400 100644 --- a/requirements/static/ci/py3.11/linux.txt +++ b/requirements/static/ci/py3.11/linux.txt @@ -21,7 +21,7 @@ annotated-types==0.6.0 # via # -c requirements/static/ci/../pkg/py3.11/linux.txt # pydantic -ansible-core==2.16.2 +ansible-core==2.16.3 # via ansible ansible==9.1.0 ; python_version >= "3.10" # via -r requirements/static/ci/linux.in @@ -33,12 +33,11 @@ asn1crypto==1.5.1 # via # certvalidator # oscrypto -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.11/linux.txt # aiohttp # jsonschema - # pytest # pytest-salt-factories # pytest-shell-utilities # pytest-skip-markers @@ -109,7 +108,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.11/linux.txt # -r requirements/base.txt @@ -378,7 +377,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.11/linux.txt # -r requirements/base.txt @@ -415,7 +414,7 @@ pytest-system-statistics==1.0.2 # via pytest-salt-factories pytest-timeout==1.4.2 # via -r requirements/pytest.txt -pytest==7.2.0 +pytest==7.3.2 # via # -r requirements/pytest.txt # pytest-custom-exit-code @@ -439,13 +438,13 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.11/linux.txt # -r requirements/base.txt python-telegram-bot==20.3 # via -r requirements/static/ci/linux.in -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.11/linux.txt # tempora @@ -466,7 +465,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.11/linux.txt # -r requirements/zeromq.txt diff --git a/requirements/static/ci/py3.11/tools.txt b/requirements/static/ci/py3.11/tools.txt index b22b0281cce..25849db76d4 100644 --- a/requirements/static/ci/py3.11/tools.txt +++ b/requirements/static/ci/py3.11/tools.txt @@ -4,7 +4,7 @@ # # pip-compile --no-emit-index-url --output-file=requirements/static/ci/py3.11/tools.txt requirements/static/ci/tools.in # -attrs==23.1.0 +attrs==23.2.0 # via # -r requirements/static/ci/tools.in # python-tools-scripts diff --git a/requirements/static/ci/py3.11/windows.txt b/requirements/static/ci/py3.11/windows.txt index 1d85ff26cbc..df58b6726f8 100644 --- a/requirements/static/ci/py3.11/windows.txt +++ b/requirements/static/ci/py3.11/windows.txt @@ -17,12 +17,11 @@ annotated-types==0.6.0 # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # pydantic -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # aiohttp # jsonschema - # pytest # pytest-salt-factories # pytest-shell-utilities # pytest-skip-markers @@ -93,7 +92,7 @@ contextvars==2.4 # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # -r requirements/base.txt @@ -303,7 +302,7 @@ pymysql==1.1.0 ; sys_platform == "win32" # -r requirements/base.txt pynacl==1.5.0 # via -r requirements/static/ci/common.in -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # -r requirements/base.txt @@ -336,7 +335,7 @@ pytest-system-statistics==1.0.2 # via pytest-salt-factories pytest-timeout==2.1.0 # via -r requirements/pytest.txt -pytest==7.2.0 +pytest==7.3.2 # via # -r requirements/pytest.txt # pytest-custom-exit-code @@ -356,21 +355,21 @@ python-dateutil==2.8.2 # moto python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # -r requirements/base.txt -pythonnet==3.0.1 ; sys_platform == "win32" +pythonnet==3.0.3 ; sys_platform == "win32" # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # tempora pyvmomi==8.0.1.0.1 # via -r requirements/static/ci/common.in -pywin32==305 ; sys_platform == "win32" +pywin32==306 ; sys_platform == "win32" # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # -r requirements/base.txt @@ -388,7 +387,7 @@ pyyaml==6.0.1 # pytest-salt-factories # responses # yamllint -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.11/windows.txt # -r requirements/zeromq.txt diff --git a/requirements/static/ci/py3.12/cloud.txt b/requirements/static/ci/py3.12/cloud.txt index 9eb9d67caa6..51a9271f1ef 100644 --- a/requirements/static/ci/py3.12/cloud.txt +++ b/requirements/static/ci/py3.12/cloud.txt @@ -30,13 +30,12 @@ asn1crypto==1.5.1 # -c requirements/static/ci/py3.12/linux.txt # certvalidator # oscrypto -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt # aiohttp # jsonschema - # pytest # pytest-salt-factories # pytest-shell-utilities # pytest-skip-markers @@ -130,7 +129,7 @@ croniter==1.3.15 ; sys_platform != "win32" # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt @@ -482,7 +481,7 @@ pynacl==1.5.0 # -c requirements/static/ci/py3.12/linux.txt # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt @@ -548,7 +547,7 @@ pytest-timeout==1.4.2 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/pytest.txt -pytest==7.2.0 +pytest==7.3.2 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/pytest.txt @@ -574,12 +573,12 @@ python-etcd==0.4.5 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt @@ -603,7 +602,7 @@ pyyaml==6.0.1 # pytest-salt-factories # responses # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt diff --git a/requirements/static/ci/py3.12/darwin.txt b/requirements/static/ci/py3.12/darwin.txt index 92553b7e88a..b5ccf298201 100644 --- a/requirements/static/ci/py3.12/darwin.txt +++ b/requirements/static/ci/py3.12/darwin.txt @@ -28,7 +28,6 @@ attrs==23.2.0 # -c requirements/static/ci/../pkg/py3.12/darwin.txt # aiohttp # jsonschema - # pytest # pytest-salt-factories # pytest-shell-utilities # pytest-skip-markers @@ -97,7 +96,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.12/darwin.txt # -r requirements/base.txt @@ -342,7 +341,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.12/darwin.txt # -r requirements/base.txt @@ -379,7 +378,7 @@ pytest-system-statistics==1.0.2 # via pytest-salt-factories pytest-timeout==1.4.2 # via -r requirements/pytest.txt -pytest==7.2.0 +pytest==7.3.2 # via # -r requirements/pytest.txt # pytest-custom-exit-code @@ -401,11 +400,11 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.12/darwin.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.12/darwin.txt # tempora @@ -424,7 +423,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.12/darwin.txt # -r requirements/zeromq.txt diff --git a/requirements/static/ci/py3.12/docs.txt b/requirements/static/ci/py3.12/docs.txt index cc8af1238d2..702a7d6a047 100644 --- a/requirements/static/ci/py3.12/docs.txt +++ b/requirements/static/ci/py3.12/docs.txt @@ -18,7 +18,7 @@ annotated-types==0.6.0 # via # -c requirements/static/ci/py3.12/linux.txt # pydantic -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/py3.12/linux.txt # aiohttp @@ -53,7 +53,7 @@ contextvars==2.4 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/base.txt @@ -187,7 +187,7 @@ pyenchant==3.2.2 # via sphinxcontrib-spelling pygments==2.15.1 # via sphinx -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/base.txt @@ -195,11 +195,11 @@ python-dateutil==2.8.2 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/py3.12/linux.txt # tempora @@ -208,7 +208,7 @@ pyyaml==6.0.1 # -c requirements/static/ci/py3.12/linux.txt # -r requirements/base.txt # myst-docutils -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/zeromq.txt diff --git a/requirements/static/ci/py3.12/freebsd.txt b/requirements/static/ci/py3.12/freebsd.txt index 45db84a606f..830bebc43e2 100644 --- a/requirements/static/ci/py3.12/freebsd.txt +++ b/requirements/static/ci/py3.12/freebsd.txt @@ -23,12 +23,11 @@ asn1crypto==1.5.1 # via # certvalidator # oscrypto -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.12/freebsd.txt # aiohttp # jsonschema - # pytest # pytest-salt-factories # pytest-shell-utilities # pytest-skip-markers @@ -96,7 +95,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.12/freebsd.txt # -r requirements/base.txt @@ -346,7 +345,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.12/freebsd.txt # -r requirements/base.txt @@ -383,7 +382,7 @@ pytest-system-statistics==1.0.2 # via pytest-salt-factories pytest-timeout==1.4.2 # via -r requirements/pytest.txt -pytest==7.2.0 +pytest==7.3.2 # via # -r requirements/pytest.txt # pytest-custom-exit-code @@ -405,11 +404,11 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.12/freebsd.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.12/freebsd.txt # tempora @@ -428,7 +427,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.12/freebsd.txt # -r requirements/zeromq.txt diff --git a/requirements/static/ci/py3.12/lint.txt b/requirements/static/ci/py3.12/lint.txt index 8c043df497f..d1208e80b39 100644 --- a/requirements/static/ci/py3.12/lint.txt +++ b/requirements/static/ci/py3.12/lint.txt @@ -26,7 +26,7 @@ annotated-types==0.6.0 # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt # pydantic -ansible-core==2.16.2 +ansible-core==2.16.3 # via # -c requirements/static/ci/py3.12/linux.txt # ansible @@ -49,7 +49,7 @@ asn1crypto==1.5.1 # oscrypto astroid==2.3.3 # via pylint -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt @@ -147,7 +147,7 @@ croniter==1.3.15 ; sys_platform != "win32" # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt @@ -518,7 +518,7 @@ pynacl==1.5.0 # -c requirements/static/ci/py3.12/linux.txt # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt @@ -555,7 +555,7 @@ python-etcd==0.4.5 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt @@ -564,7 +564,7 @@ python-telegram-bot==20.3 # via # -c requirements/static/ci/py3.12/linux.txt # -r requirements/static/ci/linux.in -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt @@ -588,7 +588,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -c requirements/static/ci/py3.12/linux.txt diff --git a/requirements/static/ci/py3.12/linux.txt b/requirements/static/ci/py3.12/linux.txt index 981dc897c1f..50d8102b62f 100644 --- a/requirements/static/ci/py3.12/linux.txt +++ b/requirements/static/ci/py3.12/linux.txt @@ -21,7 +21,7 @@ annotated-types==0.6.0 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # pydantic -ansible-core==2.16.2 +ansible-core==2.16.3 # via ansible ansible==9.1.0 ; python_version >= "3.10" # via -r requirements/static/ci/linux.in @@ -33,12 +33,11 @@ asn1crypto==1.5.1 # via # certvalidator # oscrypto -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # aiohttp # jsonschema - # pytest # pytest-salt-factories # pytest-shell-utilities # pytest-skip-markers @@ -109,7 +108,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -r requirements/base.txt @@ -378,7 +377,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -r requirements/base.txt @@ -415,7 +414,7 @@ pytest-system-statistics==1.0.2 # via pytest-salt-factories pytest-timeout==1.4.2 # via -r requirements/pytest.txt -pytest==7.2.0 +pytest==7.3.2 # via # -r requirements/pytest.txt # pytest-custom-exit-code @@ -439,13 +438,13 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -r requirements/base.txt python-telegram-bot==20.3 # via -r requirements/static/ci/linux.in -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # tempora @@ -466,7 +465,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.12/linux.txt # -r requirements/zeromq.txt diff --git a/requirements/static/ci/py3.12/tools.txt b/requirements/static/ci/py3.12/tools.txt index 90e6a047695..3e495d5a5b8 100644 --- a/requirements/static/ci/py3.12/tools.txt +++ b/requirements/static/ci/py3.12/tools.txt @@ -4,7 +4,7 @@ # # pip-compile --no-emit-index-url --output-file=requirements/static/ci/py3.12/tools.txt requirements/static/ci/tools.in # -attrs==23.1.0 +attrs==23.2.0 # via # -r requirements/static/ci/tools.in # python-tools-scripts diff --git a/requirements/static/ci/py3.12/windows.txt b/requirements/static/ci/py3.12/windows.txt index 1b9d4483014..4232423cb2c 100644 --- a/requirements/static/ci/py3.12/windows.txt +++ b/requirements/static/ci/py3.12/windows.txt @@ -17,12 +17,11 @@ annotated-types==0.6.0 # via # -c requirements/static/ci/../pkg/py3.12/windows.txt # pydantic -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.12/windows.txt # aiohttp # jsonschema - # pytest # pytest-salt-factories # pytest-shell-utilities # pytest-skip-markers @@ -93,7 +92,7 @@ contextvars==2.4 # via # -c requirements/static/ci/../pkg/py3.12/windows.txt # -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.12/windows.txt # -r requirements/base.txt @@ -303,7 +302,7 @@ pymysql==1.1.0 ; sys_platform == "win32" # -r requirements/base.txt pynacl==1.5.0 # via -r requirements/static/ci/common.in -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.12/windows.txt # -r requirements/base.txt @@ -336,7 +335,7 @@ pytest-system-statistics==1.0.2 # via pytest-salt-factories pytest-timeout==2.1.0 # via -r requirements/pytest.txt -pytest==7.2.0 +pytest==7.3.2 # via # -r requirements/pytest.txt # pytest-custom-exit-code @@ -356,7 +355,7 @@ python-dateutil==2.8.2 # moto python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.12/windows.txt # -r requirements/base.txt @@ -364,7 +363,7 @@ pythonnet==3.0.3 ; sys_platform == "win32" # via # -c requirements/static/ci/../pkg/py3.12/windows.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.12/windows.txt # tempora @@ -388,7 +387,7 @@ pyyaml==6.0.1 # pytest-salt-factories # responses # yamllint -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.12/windows.txt # -r requirements/zeromq.txt diff --git a/requirements/static/ci/py3.8/cloud.txt b/requirements/static/ci/py3.8/cloud.txt index fc20800482b..b0b2d436b88 100644 --- a/requirements/static/ci/py3.8/cloud.txt +++ b/requirements/static/ci/py3.8/cloud.txt @@ -20,7 +20,7 @@ charset-normalizer==3.2.0 # via # -c requirements/static/ci/py3.8/linux.txt # requests -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/py3.8/linux.txt # pyspnego diff --git a/requirements/static/ci/py3.8/docs.txt b/requirements/static/ci/py3.8/docs.txt index 69403f0870c..bdadbbf4a74 100644 --- a/requirements/static/ci/py3.8/docs.txt +++ b/requirements/static/ci/py3.8/docs.txt @@ -44,7 +44,7 @@ importlib-resources==5.12.0 # via # -c requirements/static/ci/py3.8/linux.txt # jaraco.text -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/py3.8/linux.txt # jaraco.text @@ -111,7 +111,7 @@ pyenchant==3.2.2 # via sphinxcontrib-spelling pygments==2.15.1 # via sphinx -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/py3.8/linux.txt # babel @@ -158,6 +158,7 @@ tempora==5.3.0 typing-extensions==4.8.0 # via # -c requirements/static/ci/py3.8/linux.txt + # inflect # pydantic uc-micro-py==1.0.2 # via linkify-it-py diff --git a/requirements/static/ci/py3.8/freebsd.txt b/requirements/static/ci/py3.8/freebsd.txt index db80875a153..f6368b8b7dc 100644 --- a/requirements/static/ci/py3.8/freebsd.txt +++ b/requirements/static/ci/py3.8/freebsd.txt @@ -23,7 +23,7 @@ async-timeout==4.0.3 # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt # aiohttp -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt # aiohttp @@ -96,7 +96,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt # -r requirements/base.txt @@ -167,7 +167,7 @@ importlib-resources==5.12.0 # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt # jaraco.text -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt # jaraco.text @@ -348,7 +348,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt # -r requirements/base.txt @@ -407,11 +407,11 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt # tempora @@ -430,7 +430,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt # -r requirements/zeromq.txt @@ -528,6 +528,7 @@ types-pyyaml==6.0.1 typing-extensions==4.8.0 # via # -c requirements/static/ci/../pkg/py3.8/freebsd.txt + # inflect # napalm # pydantic # pytest-shell-utilities diff --git a/requirements/static/ci/py3.8/linux.txt b/requirements/static/ci/py3.8/linux.txt index 336a569412e..625167aef83 100644 --- a/requirements/static/ci/py3.8/linux.txt +++ b/requirements/static/ci/py3.8/linux.txt @@ -29,7 +29,7 @@ async-timeout==4.0.3 # via # -c requirements/static/ci/../pkg/py3.8/linux.txt # aiohttp -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.8/linux.txt # aiohttp @@ -105,7 +105,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.8/linux.txt # -r requirements/base.txt @@ -186,7 +186,7 @@ importlib-resources==5.12.0 # via # -c requirements/static/ci/../pkg/py3.8/linux.txt # jaraco.text -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/../pkg/py3.8/linux.txt # jaraco.text @@ -375,7 +375,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.8/linux.txt # -r requirements/base.txt @@ -436,13 +436,13 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.8/linux.txt # -r requirements/base.txt python-telegram-bot==20.3 # via -r requirements/static/ci/linux.in -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.8/linux.txt # tempora @@ -462,7 +462,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.8/linux.txt # -r requirements/zeromq.txt @@ -580,6 +580,7 @@ twilio==8.2.2 typing-extensions==4.8.0 # via # -c requirements/static/ci/../pkg/py3.8/linux.txt + # inflect # napalm # pydantic # pytest-shell-utilities diff --git a/requirements/static/ci/py3.8/windows.txt b/requirements/static/ci/py3.8/windows.txt index a2cc8045b80..fbfb7355849 100644 --- a/requirements/static/ci/py3.8/windows.txt +++ b/requirements/static/ci/py3.8/windows.txt @@ -17,7 +17,7 @@ async-timeout==4.0.3 # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # aiohttp -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # aiohttp @@ -93,7 +93,7 @@ contextvars==2.4 # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # -r requirements/base.txt @@ -160,7 +160,7 @@ importlib-resources==5.12.0 # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # jaraco.text -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # jaraco.text @@ -305,7 +305,7 @@ pymysql==1.1.0 ; sys_platform == "win32" # -r requirements/base.txt pynacl==1.5.0 # via -r requirements/static/ci/common.in -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # -r requirements/base.txt @@ -358,15 +358,15 @@ python-dateutil==2.8.2 # moto python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # -r requirements/base.txt -pythonnet==3.0.1 ; sys_platform == "win32" +pythonnet==3.0.3 ; sys_platform == "win32" # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # tempora @@ -391,7 +391,7 @@ pyyaml==6.0.1 # pytest-salt-factories # responses # yamllint -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.8/windows.txt # -r requirements/zeromq.txt @@ -469,6 +469,7 @@ types-pyyaml==6.0.1 typing-extensions==4.8.0 # via # -c requirements/static/ci/../pkg/py3.8/windows.txt + # inflect # pydantic # pytest-shell-utilities # pytest-system-statistics diff --git a/requirements/static/ci/py3.9/cloud.txt b/requirements/static/ci/py3.9/cloud.txt index b27049dc5cc..338b4f7b952 100644 --- a/requirements/static/ci/py3.9/cloud.txt +++ b/requirements/static/ci/py3.9/cloud.txt @@ -20,7 +20,7 @@ charset-normalizer==3.2.0 # via # -c requirements/static/ci/py3.9/linux.txt # requests -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/py3.9/linux.txt # pyspnego diff --git a/requirements/static/ci/py3.9/darwin.txt b/requirements/static/ci/py3.9/darwin.txt index 38703c2f465..0d03b780c73 100644 --- a/requirements/static/ci/py3.9/darwin.txt +++ b/requirements/static/ci/py3.9/darwin.txt @@ -23,7 +23,7 @@ async-timeout==4.0.3 # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt # aiohttp -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt # aiohttp @@ -97,7 +97,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt # -r requirements/base.txt @@ -164,7 +164,7 @@ importlib-metadata==6.6.0 # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt # -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt # jaraco.text @@ -340,7 +340,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt # -r requirements/base.txt @@ -399,11 +399,11 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt # tempora @@ -422,7 +422,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt # -r requirements/zeromq.txt @@ -519,6 +519,7 @@ types-pyyaml==6.0.1 typing-extensions==4.8.0 # via # -c requirements/static/ci/../pkg/py3.9/darwin.txt + # inflect # napalm # pydantic # pytest-shell-utilities diff --git a/requirements/static/ci/py3.9/docs.txt b/requirements/static/ci/py3.9/docs.txt index f54c19643e9..a5bb29d2db3 100644 --- a/requirements/static/ci/py3.9/docs.txt +++ b/requirements/static/ci/py3.9/docs.txt @@ -40,7 +40,7 @@ importlib-metadata==6.6.0 # via # -c requirements/static/ci/py3.9/linux.txt # sphinx -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/py3.9/linux.txt # jaraco.text @@ -107,7 +107,7 @@ pyenchant==3.2.2 # via sphinxcontrib-spelling pygments==2.15.1 # via sphinx -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/py3.9/linux.txt # tempora @@ -153,6 +153,7 @@ tempora==5.3.0 typing-extensions==4.8.0 # via # -c requirements/static/ci/py3.9/linux.txt + # inflect # pydantic uc-micro-py==1.0.2 # via linkify-it-py diff --git a/requirements/static/ci/py3.9/freebsd.txt b/requirements/static/ci/py3.9/freebsd.txt index 08d22ac7528..9e9bd6f66a3 100644 --- a/requirements/static/ci/py3.9/freebsd.txt +++ b/requirements/static/ci/py3.9/freebsd.txt @@ -23,7 +23,7 @@ async-timeout==4.0.3 # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt # aiohttp -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt # aiohttp @@ -96,7 +96,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt # -r requirements/base.txt @@ -163,7 +163,7 @@ importlib-metadata==6.6.0 # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt # -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt # jaraco.text @@ -344,7 +344,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt # -r requirements/base.txt @@ -403,11 +403,11 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt # tempora @@ -426,7 +426,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt # -r requirements/zeromq.txt @@ -524,6 +524,7 @@ types-pyyaml==6.0.1 typing-extensions==4.8.0 # via # -c requirements/static/ci/../pkg/py3.9/freebsd.txt + # inflect # napalm # pydantic # pytest-shell-utilities diff --git a/requirements/static/ci/py3.9/linux.txt b/requirements/static/ci/py3.9/linux.txt index ee2670e8b28..1c527e862a9 100644 --- a/requirements/static/ci/py3.9/linux.txt +++ b/requirements/static/ci/py3.9/linux.txt @@ -29,7 +29,7 @@ async-timeout==4.0.3 # via # -c requirements/static/ci/../pkg/py3.9/linux.txt # aiohttp -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.9/linux.txt # aiohttp @@ -105,7 +105,7 @@ contextvars==2.4 # -r requirements/base.txt croniter==1.3.15 ; sys_platform != "win32" # via -r requirements/static/ci/common.in -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.9/linux.txt # -r requirements/base.txt @@ -182,7 +182,7 @@ importlib-metadata==6.6.0 # via # -c requirements/static/ci/../pkg/py3.9/linux.txt # -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/../pkg/py3.9/linux.txt # jaraco.text @@ -371,7 +371,7 @@ pynacl==1.5.0 # via # -r requirements/static/ci/common.in # paramiko -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.9/linux.txt # -r requirements/base.txt @@ -432,13 +432,13 @@ python-dateutil==2.8.2 # vcert python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.9/linux.txt # -r requirements/base.txt python-telegram-bot==20.3 # via -r requirements/static/ci/linux.in -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.9/linux.txt # tempora @@ -458,7 +458,7 @@ pyyaml==6.0.1 # responses # yamllint # yamlordereddictloader -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.9/linux.txt # -r requirements/zeromq.txt @@ -576,6 +576,7 @@ twilio==8.2.2 typing-extensions==4.8.0 # via # -c requirements/static/ci/../pkg/py3.9/linux.txt + # inflect # napalm # pydantic # pytest-shell-utilities diff --git a/requirements/static/ci/py3.9/tools.txt b/requirements/static/ci/py3.9/tools.txt index 9a423142855..f7b9f26cea9 100644 --- a/requirements/static/ci/py3.9/tools.txt +++ b/requirements/static/ci/py3.9/tools.txt @@ -4,7 +4,7 @@ # # pip-compile --no-emit-index-url --output-file=requirements/static/ci/py3.9/tools.txt requirements/static/ci/tools.in # -attrs==20.3.0 +attrs==23.2.0 # via # -r requirements/static/ci/tools.in # python-tools-scripts diff --git a/requirements/static/ci/py3.9/windows.txt b/requirements/static/ci/py3.9/windows.txt index d0f97b80de7..60b2ce6628f 100644 --- a/requirements/static/ci/py3.9/windows.txt +++ b/requirements/static/ci/py3.9/windows.txt @@ -17,7 +17,7 @@ async-timeout==4.0.3 # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # aiohttp -attrs==23.1.0 +attrs==23.2.0 # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # aiohttp @@ -93,7 +93,7 @@ contextvars==2.4 # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # -r requirements/base.txt @@ -156,7 +156,7 @@ importlib-metadata==6.6.0 # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # jaraco.text @@ -301,7 +301,7 @@ pymysql==1.1.0 ; sys_platform == "win32" # -r requirements/base.txt pynacl==1.5.0 # via -r requirements/static/ci/common.in -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # -r requirements/base.txt @@ -354,15 +354,15 @@ python-dateutil==2.8.2 # moto python-etcd==0.4.5 # via -r requirements/static/ci/common.in -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # -r requirements/base.txt -pythonnet==3.0.1 ; sys_platform == "win32" +pythonnet==3.0.3 ; sys_platform == "win32" # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # tempora @@ -387,7 +387,7 @@ pyyaml==6.0.1 # pytest-salt-factories # responses # yamllint -pyzmq==25.1.1 +pyzmq==25.1.2 # via # -c requirements/static/ci/../pkg/py3.9/windows.txt # -r requirements/zeromq.txt @@ -465,6 +465,7 @@ types-pyyaml==6.0.1 typing-extensions==4.8.0 # via # -c requirements/static/ci/../pkg/py3.9/windows.txt + # inflect # pydantic # pytest-shell-utilities # pytest-system-statistics diff --git a/requirements/static/pkg/py3.10/darwin.txt b/requirements/static/pkg/py3.10/darwin.txt index b6a856264c2..38ad70a61a4 100644 --- a/requirements/static/pkg/py3.10/darwin.txt +++ b/requirements/static/pkg/py3.10/darwin.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp async-timeout==4.0.3 # via aiohttp -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -44,7 +44,7 @@ immutables==0.15 # via contextvars importlib-metadata==6.6.0 # via -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via jaraco.text jaraco.collections==4.1.0 # via cherrypy @@ -91,17 +91,17 @@ pycryptodomex==3.19.1 # via -r requirements/crypto.txt pydantic==1.10.8 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt @@ -116,7 +116,9 @@ timelib==0.3.0 tornado==6.3.3 # via -r requirements/base.txt typing-extensions==4.8.0 - # via pydantic + # via + # inflect + # pydantic urllib3==1.26.18 # via requests yarl==1.9.4 diff --git a/requirements/static/pkg/py3.10/freebsd.txt b/requirements/static/pkg/py3.10/freebsd.txt index f5c6092fae6..1df9138f4ca 100644 --- a/requirements/static/pkg/py3.10/freebsd.txt +++ b/requirements/static/pkg/py3.10/freebsd.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp async-timeout==4.0.3 # via aiohttp -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -44,7 +44,7 @@ immutables==0.15 # via contextvars importlib-metadata==6.6.0 # via -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via jaraco.text jaraco.collections==4.1.0 # via cherrypy @@ -91,17 +91,17 @@ pycryptodomex==3.19.1 # via -r requirements/crypto.txt pydantic==1.10.8 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt @@ -116,7 +116,9 @@ timelib==0.3.0 tornado==6.3.3 # via -r requirements/base.txt typing-extensions==4.8.0 - # via pydantic + # via + # inflect + # pydantic urllib3==1.26.18 # via requests yarl==1.9.4 diff --git a/requirements/static/pkg/py3.10/linux.txt b/requirements/static/pkg/py3.10/linux.txt index 32474ce9ac9..f6ec80d42f5 100644 --- a/requirements/static/pkg/py3.10/linux.txt +++ b/requirements/static/pkg/py3.10/linux.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp async-timeout==4.0.3 # via aiohttp -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -44,7 +44,7 @@ immutables==0.15 # via contextvars importlib-metadata==6.6.0 # via -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via jaraco.text jaraco.collections==4.1.0 # via cherrypy @@ -91,17 +91,17 @@ pycryptodomex==3.19.1 # via -r requirements/crypto.txt pydantic==1.10.8 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt @@ -118,7 +118,9 @@ timelib==0.3.0 tornado==6.3.3 # via -r requirements/base.txt typing-extensions==4.8.0 - # via pydantic + # via + # inflect + # pydantic urllib3==1.26.18 # via requests yarl==1.9.4 diff --git a/requirements/static/pkg/py3.10/windows.txt b/requirements/static/pkg/py3.10/windows.txt index 42a5a8df278..7e0e6a9ef3e 100644 --- a/requirements/static/pkg/py3.10/windows.txt +++ b/requirements/static/pkg/py3.10/windows.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp async-timeout==4.0.3 # via aiohttp -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -30,7 +30,7 @@ clr-loader==0.2.6 # via pythonnet contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -48,7 +48,7 @@ immutables==0.15 # via contextvars importlib-metadata==6.6.0 # via -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via jaraco.text jaraco.collections==4.1.0 # via cherrypy @@ -101,15 +101,15 @@ pymssql==2.2.7 ; sys_platform == "win32" # via -r requirements/base.txt pymysql==1.1.0 ; sys_platform == "win32" # via -r requirements/base.txt -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pythonnet==3.0.1 ; sys_platform == "win32" +pythonnet==3.0.3 ; sys_platform == "win32" # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pywin32==306 ; sys_platform == "win32" # via @@ -117,7 +117,7 @@ pywin32==306 ; sys_platform == "win32" # wmi pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt @@ -132,7 +132,9 @@ timelib==0.3.0 tornado==6.3.3 # via -r requirements/base.txt typing-extensions==4.8.0 - # via pydantic + # via + # inflect + # pydantic urllib3==1.26.18 # via requests wmi==1.5.1 ; sys_platform == "win32" diff --git a/requirements/static/pkg/py3.11/darwin.txt b/requirements/static/pkg/py3.11/darwin.txt index 745207e3df6..782bfa74659 100644 --- a/requirements/static/pkg/py3.11/darwin.txt +++ b/requirements/static/pkg/py3.11/darwin.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp annotated-types==0.6.0 # via pydantic -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -93,17 +93,17 @@ pydantic-core==2.14.5 # via pydantic pydantic==2.5.2 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.11/freebsd.txt b/requirements/static/pkg/py3.11/freebsd.txt index 16af07ae991..2f50335e4d3 100644 --- a/requirements/static/pkg/py3.11/freebsd.txt +++ b/requirements/static/pkg/py3.11/freebsd.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp annotated-types==0.6.0 # via pydantic -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -93,17 +93,17 @@ pydantic-core==2.14.5 # via pydantic pydantic==2.5.2 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.11/linux.txt b/requirements/static/pkg/py3.11/linux.txt index e3d9550c5d2..cda0f6d7855 100644 --- a/requirements/static/pkg/py3.11/linux.txt +++ b/requirements/static/pkg/py3.11/linux.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp annotated-types==0.6.0 # via pydantic -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -93,17 +93,17 @@ pydantic-core==2.14.5 # via pydantic pydantic==2.5.2 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.11/windows.txt b/requirements/static/pkg/py3.11/windows.txt index 3f6a32e67bf..87732667a3b 100644 --- a/requirements/static/pkg/py3.11/windows.txt +++ b/requirements/static/pkg/py3.11/windows.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp annotated-types==0.6.0 # via pydantic -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -30,7 +30,7 @@ clr-loader==0.2.6 # via pythonnet contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -103,23 +103,23 @@ pymssql==2.2.7 ; sys_platform == "win32" # via -r requirements/base.txt pymysql==1.1.0 ; sys_platform == "win32" # via -r requirements/base.txt -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pythonnet==3.0.1 ; sys_platform == "win32" +pythonnet==3.0.3 ; sys_platform == "win32" # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora -pywin32==305 ; sys_platform == "win32" +pywin32==306 ; sys_platform == "win32" # via # -r requirements/base.txt # wmi pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.12/darwin.txt b/requirements/static/pkg/py3.12/darwin.txt index 294569c0d68..0887c0566ff 100644 --- a/requirements/static/pkg/py3.12/darwin.txt +++ b/requirements/static/pkg/py3.12/darwin.txt @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -93,17 +93,17 @@ pydantic-core==2.14.5 # via pydantic pydantic==2.5.2 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.12/freebsd.txt b/requirements/static/pkg/py3.12/freebsd.txt index f1f971d1d4b..e919af01af6 100644 --- a/requirements/static/pkg/py3.12/freebsd.txt +++ b/requirements/static/pkg/py3.12/freebsd.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp annotated-types==0.6.0 # via pydantic -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -93,17 +93,17 @@ pydantic-core==2.14.5 # via pydantic pydantic==2.5.2 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.12/linux.txt b/requirements/static/pkg/py3.12/linux.txt index 9e8b5285b4d..329fb2d36ec 100644 --- a/requirements/static/pkg/py3.12/linux.txt +++ b/requirements/static/pkg/py3.12/linux.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp annotated-types==0.6.0 # via pydantic -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -93,17 +93,17 @@ pydantic-core==2.14.5 # via pydantic pydantic==2.5.2 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.12/windows.txt b/requirements/static/pkg/py3.12/windows.txt index d9f9bd1f4f1..5f73e99ef6c 100644 --- a/requirements/static/pkg/py3.12/windows.txt +++ b/requirements/static/pkg/py3.12/windows.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp annotated-types==0.6.0 # via pydantic -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -30,7 +30,7 @@ clr-loader==0.2.6 # via pythonnet contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -103,15 +103,15 @@ pymssql==2.2.7 ; sys_platform == "win32" # via -r requirements/base.txt pymysql==1.1.0 ; sys_platform == "win32" # via -r requirements/base.txt -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt pythonnet==3.0.3 ; sys_platform == "win32" # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pywin32==306 ; sys_platform == "win32" # via @@ -119,7 +119,7 @@ pywin32==306 ; sys_platform == "win32" # wmi pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt diff --git a/requirements/static/pkg/py3.8/freebsd.txt b/requirements/static/pkg/py3.8/freebsd.txt index 27aa324c96d..3d3ee1a14c9 100644 --- a/requirements/static/pkg/py3.8/freebsd.txt +++ b/requirements/static/pkg/py3.8/freebsd.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp async-timeout==4.0.3 # via aiohttp -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -46,7 +46,7 @@ importlib-metadata==6.6.0 # via -r requirements/base.txt importlib-resources==5.12.0 # via jaraco.text -inflect==6.0.4 +inflect==7.0.0 # via jaraco.text jaraco.collections==4.1.0 # via cherrypy @@ -93,17 +93,17 @@ pycryptodomex==3.19.1 # via -r requirements/crypto.txt pydantic==1.10.8 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt @@ -118,7 +118,9 @@ timelib==0.3.0 tornado==6.3.3 # via -r requirements/base.txt typing-extensions==4.8.0 - # via pydantic + # via + # inflect + # pydantic urllib3==1.26.18 # via requests yarl==1.9.4 diff --git a/requirements/static/pkg/py3.8/linux.txt b/requirements/static/pkg/py3.8/linux.txt index 93d8b9a93af..7e585081e21 100644 --- a/requirements/static/pkg/py3.8/linux.txt +++ b/requirements/static/pkg/py3.8/linux.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp async-timeout==4.0.3 # via aiohttp -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -46,7 +46,7 @@ importlib-metadata==6.6.0 # via -r requirements/base.txt importlib-resources==5.12.0 # via jaraco.text -inflect==6.0.4 +inflect==7.0.0 # via jaraco.text jaraco.collections==4.1.0 # via cherrypy @@ -93,17 +93,17 @@ pycryptodomex==3.19.1 # via -r requirements/crypto.txt pydantic==1.10.8 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt @@ -120,7 +120,9 @@ timelib==0.3.0 tornado==6.3.3 # via -r requirements/base.txt typing-extensions==4.8.0 - # via pydantic + # via + # inflect + # pydantic urllib3==1.26.18 # via requests yarl==1.9.4 diff --git a/requirements/static/pkg/py3.8/windows.txt b/requirements/static/pkg/py3.8/windows.txt index 9482f57313d..30d37748787 100644 --- a/requirements/static/pkg/py3.8/windows.txt +++ b/requirements/static/pkg/py3.8/windows.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp async-timeout==4.0.3 # via aiohttp -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -30,7 +30,7 @@ clr-loader==0.2.6 # via pythonnet contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -50,7 +50,7 @@ importlib-metadata==6.6.0 # via -r requirements/base.txt importlib-resources==5.12.0 # via jaraco.text -inflect==6.0.4 +inflect==7.0.0 # via jaraco.text jaraco.collections==4.1.0 # via cherrypy @@ -103,15 +103,15 @@ pymssql==2.2.7 ; sys_platform == "win32" # via -r requirements/base.txt pymysql==1.1.0 ; sys_platform == "win32" # via -r requirements/base.txt -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pythonnet==3.0.1 ; sys_platform == "win32" +pythonnet==3.0.3 ; sys_platform == "win32" # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pywin32==306 ; sys_platform == "win32" # via @@ -120,7 +120,7 @@ pywin32==306 ; sys_platform == "win32" # wmi pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt @@ -135,7 +135,9 @@ timelib==0.3.0 tornado==6.3.3 # via -r requirements/base.txt typing-extensions==4.8.0 - # via pydantic + # via + # inflect + # pydantic urllib3==1.26.18 # via requests wmi==1.5.1 ; sys_platform == "win32" diff --git a/requirements/static/pkg/py3.9/darwin.txt b/requirements/static/pkg/py3.9/darwin.txt index 986dfd73aef..b6707a54874 100644 --- a/requirements/static/pkg/py3.9/darwin.txt +++ b/requirements/static/pkg/py3.9/darwin.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp async-timeout==4.0.3 # via aiohttp -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -44,7 +44,7 @@ immutables==0.15 # via contextvars importlib-metadata==6.6.0 # via -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via jaraco.text jaraco.collections==4.1.0 # via cherrypy @@ -91,17 +91,17 @@ pycryptodomex==3.19.1 # via -r requirements/crypto.txt pydantic==1.10.8 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt @@ -116,7 +116,9 @@ timelib==0.3.0 tornado==6.3.3 # via -r requirements/base.txt typing-extensions==4.8.0 - # via pydantic + # via + # inflect + # pydantic urllib3==1.26.18 # via requests yarl==1.9.4 diff --git a/requirements/static/pkg/py3.9/freebsd.txt b/requirements/static/pkg/py3.9/freebsd.txt index e3ef75a4e68..0a4670693c8 100644 --- a/requirements/static/pkg/py3.9/freebsd.txt +++ b/requirements/static/pkg/py3.9/freebsd.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp async-timeout==4.0.3 # via aiohttp -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -44,7 +44,7 @@ immutables==0.15 # via contextvars importlib-metadata==6.6.0 # via -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via jaraco.text jaraco.collections==4.1.0 # via cherrypy @@ -91,17 +91,17 @@ pycryptodomex==3.19.1 # via -r requirements/crypto.txt pydantic==1.10.8 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt @@ -116,7 +116,9 @@ timelib==0.3.0 tornado==6.3.3 # via -r requirements/base.txt typing-extensions==4.8.0 - # via pydantic + # via + # inflect + # pydantic urllib3==1.26.18 # via requests yarl==1.9.4 diff --git a/requirements/static/pkg/py3.9/linux.txt b/requirements/static/pkg/py3.9/linux.txt index b7a057158e6..f9d03477f6b 100644 --- a/requirements/static/pkg/py3.9/linux.txt +++ b/requirements/static/pkg/py3.9/linux.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp async-timeout==4.0.3 # via aiohttp -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -26,7 +26,7 @@ cherrypy==18.8.0 # via -r requirements/base.txt contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -44,7 +44,7 @@ immutables==0.15 # via contextvars importlib-metadata==6.6.0 # via -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via jaraco.text jaraco.collections==4.1.0 # via cherrypy @@ -91,17 +91,17 @@ pycryptodomex==3.19.1 # via -r requirements/crypto.txt pydantic==1.10.8 # via inflect -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt @@ -118,7 +118,9 @@ timelib==0.3.0 tornado==6.3.3 # via -r requirements/base.txt typing-extensions==4.8.0 - # via pydantic + # via + # inflect + # pydantic urllib3==1.26.18 # via requests yarl==1.9.4 diff --git a/requirements/static/pkg/py3.9/windows.txt b/requirements/static/pkg/py3.9/windows.txt index 831f4551836..411dc77a715 100644 --- a/requirements/static/pkg/py3.9/windows.txt +++ b/requirements/static/pkg/py3.9/windows.txt @@ -10,7 +10,7 @@ aiosignal==1.3.1 # via aiohttp async-timeout==4.0.3 # via aiohttp -attrs==23.1.0 +attrs==23.2.0 # via aiohttp autocommand==2.2.2 # via jaraco.text @@ -30,7 +30,7 @@ clr-loader==0.2.6 # via pythonnet contextvars==2.4 # via -r requirements/base.txt -cryptography==41.0.7 +cryptography==42.0.3 # via # -r requirements/base.txt # pyopenssl @@ -48,7 +48,7 @@ immutables==0.15 # via contextvars importlib-metadata==6.6.0 # via -r requirements/base.txt -inflect==6.0.4 +inflect==7.0.0 # via jaraco.text jaraco.collections==4.1.0 # via cherrypy @@ -101,15 +101,15 @@ pymssql==2.2.7 ; sys_platform == "win32" # via -r requirements/base.txt pymysql==1.1.0 ; sys_platform == "win32" # via -r requirements/base.txt -pyopenssl==23.2.0 +pyopenssl==24.0.0 # via -r requirements/base.txt python-dateutil==2.8.2 # via -r requirements/base.txt -python-gnupg==0.5.1 +python-gnupg==0.5.2 # via -r requirements/base.txt -pythonnet==3.0.1 ; sys_platform == "win32" +pythonnet==3.0.3 ; sys_platform == "win32" # via -r requirements/base.txt -pytz==2023.3.post1 +pytz==2024.1 # via tempora pywin32==306 ; sys_platform == "win32" # via @@ -118,7 +118,7 @@ pywin32==306 ; sys_platform == "win32" # wmi pyyaml==6.0.1 # via -r requirements/base.txt -pyzmq==25.1.1 +pyzmq==25.1.2 # via -r requirements/zeromq.txt requests==2.31.0 # via -r requirements/base.txt @@ -133,7 +133,9 @@ timelib==0.3.0 tornado==6.3.3 # via -r requirements/base.txt typing-extensions==4.8.0 - # via pydantic + # via + # inflect + # pydantic urllib3==1.26.18 # via requests wmi==1.5.1 ; sys_platform == "win32" diff --git a/requirements/zeromq.txt b/requirements/zeromq.txt index 38cf92e8e73..ce2710db585 100644 --- a/requirements/zeromq.txt +++ b/requirements/zeromq.txt @@ -1,4 +1,4 @@ -r base.txt -r crypto.txt -pyzmq>=24.0.0 +pyzmq>=25.1.2 diff --git a/salt/_logging/handlers.py b/salt/_logging/handlers.py index 6328df210a6..dbc0a87cb33 100644 --- a/salt/_logging/handlers.py +++ b/salt/_logging/handlers.py @@ -4,67 +4,16 @@ Salt's logging handlers """ - import logging import logging.handlers -import queue as _queue import sys from collections import deque from salt._logging.mixins import ExcInfoOnLogLevelFormatMixin -from salt.utils.versions import warn_until_date log = logging.getLogger(__name__) -class TemporaryLoggingHandler(logging.NullHandler): - """ - This logging handler will store all the log records up to its maximum - queue size at which stage the first messages stored will be dropped. - - Should only be used as a temporary logging handler, while the logging - system is not fully configured. - - Once configured, pass any logging handlers that should have received the - initial log messages to the function - :func:`TemporaryLoggingHandler.sync_with_handlers` and all stored log - records will be dispatched to the provided handlers. - - .. versionadded:: 0.17.0 - """ - - def __init__(self, level=logging.NOTSET, max_queue_size=10000): - warn_until_date( - "20240101", - "Please stop using '{name}.TemporaryLoggingHandler'. " - "'{name}.TemporaryLoggingHandler' will go away after " - "{{date}}.".format(name=__name__), - ) - super().__init__(level=level) - self.__messages = deque(maxlen=max_queue_size) - - def handle(self, record): - self.acquire() - self.__messages.append(record) - self.release() - - def sync_with_handlers(self, handlers=()): - """ - Sync the stored log records to the provided log handlers. - """ - if not handlers: - return - - while self.__messages: - record = self.__messages.popleft() - for handler in handlers: - if handler.level > record.levelno: - # If the handler's level is higher than the log record one, - # it should not handle the log record - continue - handler.handle(record) - - class StreamHandler(ExcInfoOnLogLevelFormatMixin, logging.StreamHandler): """ Stream handler which properly handles exc_info on a per handler basis @@ -214,33 +163,3 @@ class WatchedFileHandler( """ Watched file handler which properly handles exc_info on a per handler basis """ - - -class QueueHandler( - ExcInfoOnLogLevelFormatMixin, logging.handlers.QueueHandler -): # pylint: disable=no-member,inconsistent-mro - def __init__(self, queue): # pylint: disable=useless-super-delegation - super().__init__(queue) - warn_until_date( - "20240101", - "Please stop using '{name}.QueueHandler' and instead " - "use 'logging.handlers.QueueHandler'. " - "'{name}.QueueHandler' will go away after " - "{{date}}.".format(name=__name__), - ) - - def enqueue(self, record): - """ - Enqueue a record. - - The base implementation uses put_nowait. You may want to override - this method if you want to use blocking, timeouts or custom queue - implementations. - """ - try: - self.queue.put_nowait(record) - except _queue.Full: - sys.stderr.write( - "[WARNING ] Message queue is full, " - 'unable to write "{}" to log.\n'.format(record) - ) diff --git a/salt/_logging/impl.py b/salt/_logging/impl.py index 7b3ab99631a..237a8a5c173 100644 --- a/salt/_logging/impl.py +++ b/salt/_logging/impl.py @@ -420,6 +420,7 @@ def set_logging_options_dict(opts): except AttributeError: pass set_logging_options_dict.__options_dict__ = opts + set_lowest_log_level_by_opts(opts) def freeze_logging_options_dict(): diff --git a/salt/channel/client.py b/salt/channel/client.py index 4368beb73df..ea42cefaef0 100644 --- a/salt/channel/client.py +++ b/salt/channel/client.py @@ -3,8 +3,6 @@ Encapsulate the different transports available to Salt. This includes client side transport, for the ReqServer and the Publisher """ - - import logging import os import time @@ -605,14 +603,22 @@ class AsyncPubChannel: def _decode_payload(self, payload): # we need to decrypt it log.trace("Decoding payload: %s", payload) + reauth = False if payload["enc"] == "aes": self._verify_master_signature(payload) try: payload["load"] = self.auth.crypticle.loads(payload["load"]) except salt.crypt.AuthenticationError: - yield self.auth.authenticate() - payload["load"] = self.auth.crypticle.loads(payload["load"]) - + reauth = True + if reauth: + try: + yield self.auth.authenticate() + payload["load"] = self.auth.crypticle.loads(payload["load"]) + except salt.crypt.AuthenticationError: + log.error( + "Payload decryption failed even after re-authenticating with master %s", + self.opts["master_ip"], + ) raise tornado.gen.Return(payload) def __enter__(self): diff --git a/salt/channel/server.py b/salt/channel/server.py index a2cc3d5d5ec..9be0178342a 100644 --- a/salt/channel/server.py +++ b/salt/channel/server.py @@ -56,10 +56,6 @@ class ReqServerChannel: transport = salt.transport.request_server(opts, **kwargs) return cls(opts, transport) - @staticmethod - def _clean_key(key): - return key.strip().replace("\r", "").replace("\n", "") - def __init__(self, opts, transport): self.opts = opts self.transport = transport @@ -385,7 +381,7 @@ class ReqServerChannel: elif os.path.isfile(pubfn): # The key has been accepted, check it with salt.utils.files.fopen(pubfn, "r") as pubfn_handle: - if self._clean_key(pubfn_handle.read()) != self._clean_key(load["pub"]): + if salt.crypt.clean_key(pubfn_handle.read()) != load["pub"]: log.error( "Authentication attempt from %s failed, the public " "keys did not match. This may be an attempt to compromise " @@ -494,9 +490,7 @@ class ReqServerChannel: # case. Otherwise log the fact that the minion is still # pending. with salt.utils.files.fopen(pubfn_pend, "r") as pubfn_handle: - if self._clean_key(pubfn_handle.read()) != self._clean_key( - load["pub"] - ): + if salt.crypt.clean_key(pubfn_handle.read()) != load["pub"]: log.error( "Authentication attempt from %s failed, the public " "key in pending did not match. This may be an " @@ -552,9 +546,7 @@ class ReqServerChannel: # so, pass on doing anything here, and let it get automatically # accepted below. with salt.utils.files.fopen(pubfn_pend, "r") as pubfn_handle: - if self._clean_key(pubfn_handle.read()) != self._clean_key( - load["pub"] - ): + if salt.crypt.clean_key(pubfn_handle.read()) != load["pub"]: log.error( "Authentication attempt from %s failed, the public " "keys in pending did not match. This may be an " diff --git a/salt/client/__init__.py b/salt/client/__init__.py index 013c860ff69..9a1af36c872 100644 --- a/salt/client/__init__.py +++ b/salt/client/__init__.py @@ -1203,7 +1203,7 @@ class LocalClient: continue # Anything below this point is expected to be a job return event. - if not raw["tag"].startswith(f"salt/job/{jid}/ret"): + if not raw["tag"].startswith(f"salt/job/{jid}/ret/"): log.debug("Skipping non return event: %s", raw["tag"]) continue if "return" not in raw["data"]: diff --git a/salt/config/__init__.py b/salt/config/__init__.py index e5e94be5496..869d92ce7df 100644 --- a/salt/config/__init__.py +++ b/salt/config/__init__.py @@ -1066,7 +1066,7 @@ DEFAULT_MINION_OPTS = immutabletypes.freeze( "pillar_cache": False, "pillar_cache_ttl": 3600, "pillar_cache_backend": "disk", - "request_channel_timeout": 30, + "request_channel_timeout": 60, "request_channel_tries": 3, "gpg_cache": False, "gpg_cache_ttl": 86400, @@ -3963,6 +3963,14 @@ def apply_master_config(overrides=None, defaults=None): opts = defaults.copy() opts["__role"] = "master" + + # Suppress fileserver update in FSChan, for FSClient instances generated + # during Pillar compilation. The master daemon already handles FS updates + # in its maintenance thread. Refreshing during Pillar compilation slows + # down Pillar considerably (even to the point of timeout) when there are + # many gitfs remotes. + opts["__fs_update"] = True + _adjust_log_file_override(overrides, defaults["log_file"]) if overrides: opts.update(overrides) diff --git a/salt/crypt.py b/salt/crypt.py index ce3a5a83c67..ca03c5ecadc 100644 --- a/salt/crypt.py +++ b/salt/crypt.py @@ -83,6 +83,13 @@ if not HAS_M2 and not HAS_CRYPTO: log = logging.getLogger(__name__) +def clean_key(key): + """ + Clean the key so that it only has unix style line endings (\\n) + """ + return "\n".join(key.strip().splitlines()) + + def read_dropfile(cachedir): dfn = os.path.join(cachedir, ".dfn") try: @@ -273,7 +280,7 @@ def _get_key_with_evict(path, timestamp, passphrase): Load a private key from disk. `timestamp` above is intended to be the timestamp of the file's last modification. This fn is memoized so if it is called with the same path and timestamp (the file's last modified time) the - second time the result is returned from the memoiziation. If the file gets + second time the result is returned from the memoization. If the file gets modified then the params are different and the key is loaded from disk. """ log.debug("salt.crypt._get_key_with_evict: Loading private key") @@ -489,7 +496,7 @@ class MasterKeys(dict): ) if os.path.isfile(self.sig_path): with salt.utils.files.fopen(self.sig_path) as fp_: - self.pub_signature = fp_.read() + self.pub_signature = clean_key(fp_.read()) log.info( "Read %s's signature from %s", os.path.basename(self.pub_path), @@ -607,7 +614,7 @@ class MasterKeys(dict): with salt.utils.files.fopen(path, "wb+") as wfh: wfh.write(key.publickey().exportKey("PEM")) with salt.utils.files.fopen(path) as rfh: - return rfh.read() + return clean_key(rfh.read()) def get_ckey_paths(self): return self.cluster_pub_path, self.cluster_rsa_path @@ -1011,11 +1018,11 @@ class AsyncAuth: "clean out the keys. The Salt Minion will now exit." ) # Add a random sleep here for systems that are using a - # a service manager to immediately restart the service - # to avoid overloading the system + # service manager to immediately restart the service to + # avoid overloading the system time.sleep(random.randint(10, 20)) sys.exit(salt.defaults.exitcodes.EX_NOPERM) - # has the master returned that its maxed out with minions? + # Has the master returned that it's maxed out with minions? elif payload["ret"] == "full": return "full" else: @@ -1117,7 +1124,7 @@ class AsyncAuth: except Exception: # pylint: disable=broad-except pass with salt.utils.files.fopen(self.pub_path) as f: - payload["pub"] = f.read() + payload["pub"] = clean_key(f.read()) return payload def decrypt_aes(self, payload, master_pub=True): @@ -1340,11 +1347,9 @@ class AsyncAuth: m_pub_exists = os.path.isfile(m_pub_fn) if m_pub_exists and master_pub and not self.opts["open_mode"]: with salt.utils.files.fopen(m_pub_fn) as fp_: - local_master_pub = fp_.read() + local_master_pub = clean_key(fp_.read()) - if payload["pub_key"].replace("\n", "").replace( - "\r", "" - ) != local_master_pub.replace("\n", "").replace("\r", ""): + if payload["pub_key"] != local_master_pub: if not self.check_auth_deps(payload): return "" diff --git a/salt/fileserver/s3fs.py b/salt/fileserver/s3fs.py index e4ad5e5c6b5..d013ea3b193 100644 --- a/salt/fileserver/s3fs.py +++ b/salt/fileserver/s3fs.py @@ -104,6 +104,8 @@ import salt.utils.versions log = logging.getLogger(__name__) +S3_HASH_TYPE = "md5" + def envs(): """ @@ -134,7 +136,7 @@ def update(): cached_file_path = _get_cached_file_name( bucket, saltenv, file_path ) - log.info("%s - %s : %s", bucket, saltenv, file_path) + log.debug("%s - %s : %s", bucket, saltenv, file_path) # load the file from S3 if it's not in the cache or it's old _get_file_from_s3( @@ -189,7 +191,7 @@ def find_file(path, saltenv="base", **kwargs): def file_hash(load, fnd): """ - Return an MD5 file hash + Return the hash of an object's cached copy """ if "env" in load: # "env" is not supported; Use "saltenv". @@ -208,8 +210,8 @@ def file_hash(load, fnd): ) if os.path.isfile(cached_file_path): - ret["hsum"] = salt.utils.hashutils.get_hash(cached_file_path) - ret["hash_type"] = "md5" + ret["hash_type"] = S3_HASH_TYPE + ret["hsum"] = salt.utils.hashutils.get_hash(cached_file_path, S3_HASH_TYPE) return ret @@ -716,11 +718,15 @@ def _get_file_from_s3(metadata, saltenv, bucket_name, path, cached_file_path): if file_etag.find("-") == -1: file_md5 = file_etag - cached_md5 = salt.utils.hashutils.get_hash(cached_file_path, "md5") + cached_md5 = salt.utils.hashutils.get_hash( + cached_file_path, S3_HASH_TYPE + ) # hashes match we have a cache hit if cached_md5 == file_md5: return + else: + log.info(f"found different hash for file {path}, updating...") else: cached_file_stat = os.stat(cached_file_path) cached_file_size = cached_file_stat.st_size diff --git a/salt/grains/core.py b/salt/grains/core.py index ddd479fce32..4050e7f8251 100644 --- a/salt/grains/core.py +++ b/salt/grains/core.py @@ -730,46 +730,62 @@ def _windows_virtual(osdata): if osdata["kernel"] != "Windows": return grains - grains["virtual"] = osdata.get("virtual", "physical") + # Set the default virtual environment to physical, meaning not a VM + grains["virtual"] = "physical" - # It is possible that the 'manufacturer' and/or 'productname' grains - # exist but have a value of None. + # It is possible that the 'manufacturer' and/or 'productname' grains exist + # but have a value of None manufacturer = osdata.get("manufacturer", "") if manufacturer is None: manufacturer = "" - productname = osdata.get("productname", "") - if productname is None: - productname = "" + product_name = osdata.get("productname", "") + if product_name is None: + product_name = "" + bios_string = osdata.get("biosstring", "") + if bios_string is None: + bios_string = "" if "QEMU" in manufacturer: # FIXME: Make this detect between kvm or qemu grains["virtual"] = "kvm" - if "Bochs" in manufacturer: + elif "VRTUAL" in bios_string: # (not a typo) + grains["virtual"] = "HyperV" + elif "A M I" in bios_string: + grains["virtual"] = "VirtualPC" + elif "Xen" in bios_string: + grains["virtual"] = "Xen" + if "HVM domU" in product_name: + grains["virtual_subtype"] = "HVM domU" + elif "AMAZON" in bios_string: + grains["virtual"] = "EC2" + elif "Bochs" in manufacturer: grains["virtual"] = "kvm" # Product Name: (oVirt) www.ovirt.org # Red Hat Community virtualization Project based on kvm - elif "oVirt" in productname: + elif "oVirt" in product_name: grains["virtual"] = "kvm" grains["virtual_subtype"] = "oVirt" # Red Hat Enterprise Virtualization - elif "RHEV Hypervisor" in productname: + elif "RHEV Hypervisor" in product_name: grains["virtual"] = "kvm" grains["virtual_subtype"] = "rhev" # Product Name: VirtualBox - elif "VirtualBox" in productname: + elif "VirtualBox" in product_name: grains["virtual"] = "VirtualBox" # Product Name: VMware Virtual Platform - elif "VMware" in productname: + elif "VMware" in product_name: grains["virtual"] = "VMware" # Manufacturer: Microsoft Corporation # Product Name: Virtual Machine - elif "Microsoft" in manufacturer and "Virtual Machine" in productname: + elif "Microsoft" in manufacturer and "Virtual Machine" in product_name: grains["virtual"] = "VirtualPC" + elif "OpenStack" in product_name: + grains["virtual"] = "OpenStack" # Manufacturer: Parallels Software International Inc. elif "Parallels" in manufacturer: grains["virtual"] = "Parallels" # Apache CloudStack - elif "CloudStack KVM Hypervisor" in productname: + elif "CloudStack KVM Hypervisor" in product_name: grains["virtual"] = "kvm" grains["virtual_subtype"] = "cloudstack" return grains @@ -1498,88 +1514,143 @@ def _windows_platform_data(): if not HAS_WMI: return {} + grains = {} with salt.utils.winapi.Com(): wmi_c = wmi.WMI() - # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394102%28v=vs.85%29.aspx - systeminfo = wmi_c.Win32_ComputerSystem()[0] - # https://msdn.microsoft.com/en-us/library/aa394239(v=vs.85).aspx - osinfo = wmi_c.Win32_OperatingSystem()[0] - # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394077(v=vs.85).aspx - biosinfo = wmi_c.Win32_BIOS()[0] - # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394498(v=vs.85).aspx - timeinfo = wmi_c.Win32_TimeZone()[0] - # https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystemproduct - csproductinfo = wmi_c.Win32_ComputerSystemProduct()[0] + try: + # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394102%28v=vs.85%29.aspx + systeminfo = wmi_c.Win32_ComputerSystem()[0] + grains.update( + { + "manufacturer": _clean_value( + "manufacturer", systeminfo.Manufacturer + ), + "productname": _clean_value("productname", systeminfo.Model), + } + ) + except IndexError: + grains.update({"manufacturer": None, "productname": None}) + log.warning("Computer System info not available on this system") + + try: + # https://msdn.microsoft.com/en-us/library/aa394239(v=vs.85).aspx + osinfo = wmi_c.Win32_OperatingSystem()[0] + os_release = _windows_os_release_grain( + caption=osinfo.Caption, product_type=osinfo.ProductType + ) + grains.update( + { + "kernelrelease": _clean_value("kernelrelease", osinfo.Version), + "osfullname": _clean_value("osfullname", osinfo.Caption), + "osmanufacturer": _clean_value( + "osmanufacturer", osinfo.Manufacturer + ), + "osrelease": _clean_value("osrelease", os_release), + "osversion": _clean_value("osversion", osinfo.Version), + } + ) + except IndexError: + grains.update( + { + "kernelrelease": None, + "osfullname": None, + "osmanufacturer": None, + "osrelease": None, + "osversion": None, + } + ) + log.warning("Operating System info not available on this system") + + try: + # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394077(v=vs.85).aspx + biosinfo = wmi_c.Win32_BIOS()[0] + grains.update( + { + # bios name had a bunch of whitespace appended to it in my testing + # 'PhoenixBIOS 4.0 Release 6.0 ' + "biosversion": _clean_value("biosversion", biosinfo.Name.strip()), + "biosstring": _clean_value("string", biosinfo.Version), + "serialnumber": _clean_value("serialnumber", biosinfo.SerialNumber), + } + ) + except IndexError: + grains.update( + {"biosstring": None, "biosversion": None, "serialnumber": None} + ) + log.warning("BIOS info not available on this system") + + try: + # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394498(v=vs.85).aspx + timeinfo = wmi_c.Win32_TimeZone()[0] + grains.update( + { + "timezone": _clean_value("timezone", timeinfo.Description), + } + ) + except IndexError: + grains.update({"timezone": None}) + log.warning("TimeZone info not available on this system") + + try: + # https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-computersystemproduct + csproductinfo = wmi_c.Win32_ComputerSystemProduct()[0] + grains.update( + { + "uuid": _clean_value("uuid", csproductinfo.UUID.lower()), + } + ) + except IndexError: + grains.update({"uuid": None}) + log.warning("Computer System Product info not available on this system") # http://msdn.microsoft.com/en-us/library/windows/desktop/aa394072(v=vs.85).aspx - motherboard = {"product": None, "serial": None} try: motherboardinfo = wmi_c.Win32_BaseBoard()[0] - motherboard["product"] = motherboardinfo.Product - motherboard["serial"] = motherboardinfo.SerialNumber + grains.update( + { + "motherboard": { + "productname": _clean_value( + "motherboard.productname", motherboardinfo.Product + ), + "serialnumber": _clean_value( + "motherboard.serialnumber", motherboardinfo.SerialNumber + ), + }, + } + ) except IndexError: + grains.update( + { + "motherboard": {"productname": None, "serialnumber": None}, + } + ) log.debug("Motherboard info not available on this system") - kernel_version = platform.version() - info = salt.utils.win_osinfo.get_os_version_info() + grains.update( + { + "kernelversion": _clean_value("kernelversion", platform.version()), + } + ) net_info = salt.utils.win_osinfo.get_join_info() - - service_pack = None - if info["ServicePackMajor"] > 0: - service_pack = "".join(["SP", str(info["ServicePackMajor"])]) - - os_release = _windows_os_release_grain( - caption=osinfo.Caption, product_type=osinfo.ProductType + grains.update( + { + "windowsdomain": _clean_value("windowsdomain", net_info["Domain"]), + "windowsdomaintype": _clean_value( + "windowsdomaintype", net_info["DomainType"] + ), + } ) - grains = { - "kernelrelease": _clean_value("kernelrelease", osinfo.Version), - "kernelversion": _clean_value("kernelversion", kernel_version), - "osversion": _clean_value("osversion", osinfo.Version), - "osrelease": _clean_value("osrelease", os_release), - "osservicepack": _clean_value("osservicepack", service_pack), - "osmanufacturer": _clean_value("osmanufacturer", osinfo.Manufacturer), - "manufacturer": _clean_value("manufacturer", systeminfo.Manufacturer), - "productname": _clean_value("productname", systeminfo.Model), - # bios name had a bunch of whitespace appended to it in my testing - # 'PhoenixBIOS 4.0 Release 6.0 ' - "biosversion": _clean_value("biosversion", biosinfo.Name.strip()), - "serialnumber": _clean_value("serialnumber", biosinfo.SerialNumber), - "osfullname": _clean_value("osfullname", osinfo.Caption), - "timezone": _clean_value("timezone", timeinfo.Description), - "uuid": _clean_value("uuid", csproductinfo.UUID.lower()), - "windowsdomain": _clean_value("windowsdomain", net_info["Domain"]), - "windowsdomaintype": _clean_value( - "windowsdomaintype", net_info["DomainType"] - ), - "motherboard": { - "productname": _clean_value( - "motherboard.productname", motherboard["product"] - ), - "serialnumber": _clean_value( - "motherboard.serialnumber", motherboard["serial"] - ), - }, - } - - # test for virtualized environments - # I only had VMware available so the rest are unvalidated - if "VRTUAL" in biosinfo.Version: # (not a typo) - grains["virtual"] = "HyperV" - elif "A M I" in biosinfo.Version: - grains["virtual"] = "VirtualPC" - elif "VMware" in systeminfo.Model: - grains["virtual"] = "VMware" - elif "VirtualBox" in systeminfo.Model: - grains["virtual"] = "VirtualBox" - elif "Xen" in biosinfo.Version: - grains["virtual"] = "Xen" - if "HVM domU" in systeminfo.Model: - grains["virtual_subtype"] = "HVM domU" - elif "OpenStack" in systeminfo.Model: - grains["virtual"] = "OpenStack" - elif "AMAZON" in biosinfo.Version: - grains["virtual"] = "EC2" + info = salt.utils.win_osinfo.get_os_version_info() + if info["ServicePackMajor"] > 0: + service_pack = "".join(["SP", str(info["ServicePackMajor"])]) + grains.update( + { + "osservicepack": _clean_value("osservicepack", service_pack), + } + ) + else: + grains.update({"osservicepack": None}) return grains diff --git a/salt/loader/__init__.py b/salt/loader/__init__.py index bd542dfb5a5..160fe8e60d0 100644 --- a/salt/loader/__init__.py +++ b/salt/loader/__init__.py @@ -20,7 +20,6 @@ import salt.syspaths import salt.utils.context import salt.utils.data import salt.utils.dictupdate -import salt.utils.event import salt.utils.files import salt.utils.lazy import salt.utils.odict @@ -358,6 +357,8 @@ def minion_mods( ret[f_key] = funcs[func] if notify: + import salt.utils.event + with salt.utils.event.get_event("minion", opts=opts, listen=False) as evt: evt.fire_event( {"complete": True}, tag=salt.defaults.events.MINION_MOD_REFRESH_COMPLETE @@ -534,6 +535,7 @@ def utils( whitelist=None, context=None, proxy=None, + file_client=None, pack_self=None, loaded_base_name=None, ): @@ -553,7 +555,11 @@ def utils( opts, tag="utils", whitelist=whitelist, - pack={"__context__": context, "__proxy__": proxy or {}}, + pack={ + "__context__": context, + "__proxy__": proxy or {}, + "__file_client__": file_client, + }, pack_self=pack_self, loaded_base_name=loaded_base_name, _only_pack_properly_namespaced_functions=False, @@ -884,7 +890,9 @@ def log_handlers(opts, loaded_base_name=None): return FilterDictWrapper(ret, ".setup_handlers") -def ssh_wrapper(opts, functions=None, context=None, loaded_base_name=None): +def ssh_wrapper( + opts, functions=None, context=None, file_client=None, loaded_base_name=None +): """ Returns the custom logging handler modules @@ -902,13 +910,23 @@ def ssh_wrapper(opts, functions=None, context=None, loaded_base_name=None): ), opts, tag="wrapper", - pack={"__salt__": functions, "__context__": context}, + pack={ + "__salt__": functions, + "__context__": context, + "__file_client__": file_client, + }, loaded_base_name=loaded_base_name, ) def render( - opts, functions, states=None, proxy=None, context=None, loaded_base_name=None + opts, + functions, + states=None, + proxy=None, + context=None, + file_client=None, + loaded_base_name=None, ): """ Returns the render modules @@ -929,6 +947,7 @@ def render( "__salt__": functions, "__grains__": opts.get("grains", {}), "__context__": context, + "__file_client__": file_client, } if states: diff --git a/salt/loader/dunder.py b/salt/loader/dunder.py index 5fae9014797..f0235d19a29 100644 --- a/salt/loader/dunder.py +++ b/salt/loader/dunder.py @@ -6,4 +6,4 @@ import salt.loader.context loader_context = salt.loader.context.LoaderContext() -__file_client__ = loader_context.named_context("__file_client__") +__file_client__ = loader_context.named_context("__file_client__", default=None) diff --git a/salt/log/__init__.py b/salt/log/__init__.py deleted file mode 100644 index 45202cffb0f..00000000000 --- a/salt/log/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -""" - :codeauthor: Pedro Algarvio (pedro@algarvio.me) - - - salt.log - ~~~~~~~~ - - This is where Salt's logging gets set up. Currently, the required imports - are made to assure backwards compatibility. -""" - -# pylint: disable = no-name-in-module - -# Import several classes/functions from salt.log.setup for backwards compatibility -from salt._logging import LOG_LEVELS, SORTED_LEVEL_NAMES -from salt.log.setup import ( - is_console_configured, - is_logfile_configured, - is_logging_configured, - is_temp_logging_configured, - set_logger_level, - setup_console_logger, - setup_logfile_logger, - setup_temp_logger, -) -from salt.utils.versions import warn_until_date - -warn_until_date( - "20240101", - "Please stop using '{name}' and instead use 'salt._logging'. " - "'{name}' will go away after {{date}}.".format(name=__name__), - stacklevel=3, -) diff --git a/salt/log/handlers/__init__.py b/salt/log/handlers/__init__.py deleted file mode 100644 index 8bc740e20f1..00000000000 --- a/salt/log/handlers/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -import logging - -from salt._logging.handlers import ( - FileHandler, - QueueHandler, - RotatingFileHandler, - StreamHandler, - SysLogHandler, - TemporaryLoggingHandler, - WatchedFileHandler, -) -from salt.utils.versions import warn_until_date - -warn_until_date( - "20240101", - "Please stop using '{name}' and instead use 'salt._logging.handlers'. " - "'{name}' will go away after {{date}}.".format(name=__name__), -) - -NullHandler = logging.NullHandler diff --git a/salt/log/mixins.py b/salt/log/mixins.py deleted file mode 100644 index 6619b564198..00000000000 --- a/salt/log/mixins.py +++ /dev/null @@ -1,17 +0,0 @@ -# pylint: disable=unused-import -from salt._logging.mixins import ( - ExcInfoOnLogLevelFormatMixin as ExcInfoOnLogLevelFormatMixIn, -) -from salt._logging.mixins import LoggingGarbageMixin as LoggingGarbageMixIn -from salt._logging.mixins import LoggingMixinMeta as LoggingMixInMeta -from salt._logging.mixins import LoggingProfileMixin as LoggingProfileMixIn -from salt._logging.mixins import LoggingTraceMixin as LoggingTraceMixIn -from salt.utils.versions import warn_until_date - -# pylint: enable=unused-import - -warn_until_date( - "20240101", - "Please stop using '{name}' and instead use 'salt._logging.mixins'. " - "'{name}' will go away after {{date}}.".format(name=__name__), -) diff --git a/salt/log/setup.py b/salt/log/setup.py deleted file mode 100644 index 74bd7bbd3e1..00000000000 --- a/salt/log/setup.py +++ /dev/null @@ -1,201 +0,0 @@ -# pylint: disable=unused-import -from functools import wraps - -from salt._logging.handlers import ( - FileHandler, - QueueHandler, - RotatingFileHandler, - StreamHandler, - SysLogHandler, - WatchedFileHandler, -) -from salt._logging.impl import ( - LOG_COLORS, - LOG_LEVELS, - LOG_VALUES_TO_LEVELS, - SORTED_LEVEL_NAMES, - SaltColorLogRecord, - SaltLogRecord, -) -from salt._logging.impl import set_log_record_factory as setLogRecordFactory -from salt.utils.versions import warn_until_date - -warn_until_date( - "20240101", - "Please stop using '{name}' and instead use 'salt._logging'. " - "'{name}' will go away after {{date}}. Do note however that " - "'salt._logging' is now considered a non public implementation " - "and is subject to change without deprecations.".format(name=__name__), - stacklevel=4, -) - - -def _deprecated_warning(func): - @wraps(func) - def wrapper(*args, **kwargs): - warn_until_date( - "20240101", - "Please stop using 'salt.log.setup.{name}()' as it no longer does anything and " - "will go away after {{date}}.".format(name=func.__qualname__), - stacklevel=4, - ) - - return wrapper - - -@_deprecated_warning -def is_console_configured(): - pass - - -@_deprecated_warning -def is_logfile_configured(): - pass - - -@_deprecated_warning -def is_logging_configured(): - pass - - -@_deprecated_warning -def is_temp_logging_configured(): - pass - - -@_deprecated_warning -def is_mp_logging_listener_configured(): - pass - - -@_deprecated_warning -def is_mp_logging_configured(): - pass - - -@_deprecated_warning -def is_extended_logging_configured(): - pass - - -class SaltLogQueueHandler(QueueHandler): - """ - Subclassed just to differentiate when debugging - """ - - -@_deprecated_warning -def getLogger(name): - pass - - -@_deprecated_warning -def setup_temp_logger(log_level="error"): - pass - - -@_deprecated_warning -def setup_console_logger(log_level="error", log_format=None, date_format=None): - pass - - -@_deprecated_warning -def setup_logfile_logger( - log_path, - log_level="error", - log_format=None, - date_format=None, - max_bytes=0, - backup_count=0, -): - pass - - -@_deprecated_warning -def setup_extended_logging(opts): - pass - - -@_deprecated_warning -def get_multiprocessing_logging_queue(): - pass - - -@_deprecated_warning -def set_multiprocessing_logging_queue(queue): - pass - - -@_deprecated_warning -def get_multiprocessing_logging_level(): - pass - - -@_deprecated_warning -def set_multiprocessing_logging_level(log_level): - pass - - -@_deprecated_warning -def set_multiprocessing_logging_level_by_opts(opts): - pass - - -@_deprecated_warning -def setup_multiprocessing_logging(queue=None): - pass - - -@_deprecated_warning -def shutdown_console_logging(): - pass - - -@_deprecated_warning -def shutdown_logfile_logging(): - pass - - -@_deprecated_warning -def shutdown_temp_logging(): - pass - - -@_deprecated_warning -def shutdown_multiprocessing_logging(): - pass - - -@_deprecated_warning -def shutdown_multiprocessing_logging_listener(daemonizing=False): - pass - - -@_deprecated_warning -def set_logger_level(logger_name, log_level="error"): - pass - - -@_deprecated_warning -def patch_python_logging_handlers(): - pass - - -@_deprecated_warning -def __process_multiprocessing_logging_queue(opts, queue): - pass - - -@_deprecated_warning -def __remove_null_logging_handler(): - pass - - -@_deprecated_warning -def __remove_queue_logging_handler(): - pass - - -@_deprecated_warning -def __remove_temp_logging_handler(): - pass diff --git a/salt/minion.py b/salt/minion.py index ad23f46e1f5..729053ed402 100644 --- a/salt/minion.py +++ b/salt/minion.py @@ -1149,11 +1149,13 @@ class MinionManager(MinionBase): self.minions.append(minion) break except SaltClientError as exc: + minion.destroy() failed = True log.error( "Error while bringing up minion for multi-master. Is " - "master at %s responding?", + "master at %s responding? The error message was %s", minion.opts["master"], + exc, exc_info=True, ) last = time.time() @@ -1161,6 +1163,7 @@ class MinionManager(MinionBase): auth_wait += self.auth_wait await asyncio.sleep(auth_wait) except SaltMasterUnresolvableError: + minion.destroy() err = ( "Master address: '{}' could not be resolved. Invalid or" " unresolveable address. Set 'master' value in minion config.".format( @@ -1170,6 +1173,7 @@ class MinionManager(MinionBase): log.error(err) break except Exception as e: # pylint: disable=broad-except + minion.destroy() failed = True log.critical( "Unexpected error while connecting to %s", @@ -3281,19 +3285,14 @@ class Minion(MinionBase): """ Tear down the minion """ - if self._running is False: - return - self._running = False if hasattr(self, "schedule"): del self.schedule if hasattr(self, "pub_channel") and self.pub_channel is not None: self.pub_channel.on_recv(None) self.pub_channel.close() - del self.pub_channel - if hasattr(self, "req_channel") and self.req_channel: + if hasattr(self, "req_channel") and self.req_channel is not None: self.req_channel.close() - self.req_channel = None if hasattr(self, "periodic_callbacks"): for cb in self.periodic_callbacks.values(): cb.stop() diff --git a/salt/modules/cassandra_mod.py b/salt/modules/cassandra_mod.py deleted file mode 100644 index 029fd08fb9b..00000000000 --- a/salt/modules/cassandra_mod.py +++ /dev/null @@ -1,218 +0,0 @@ -""" - -.. warning:: - - The `cassandra` module is deprecated in favor of the `cassandra_cql` - module. - -Cassandra NoSQL Database Module - -:depends: - pycassa Cassandra Python adapter -:configuration: - The location of the 'nodetool' command, host, and thrift port needs to be - specified via pillar:: - - cassandra.nodetool: /usr/local/bin/nodetool - cassandra.host: localhost - cassandra.thrift_port: 9160 -""" - -import logging - -import salt.utils.path -from salt.utils.versions import warn_until_date - -log = logging.getLogger(__name__) - - -HAS_PYCASSA = False -try: - from pycassa.system_manager import SystemManager - - HAS_PYCASSA = True -except ImportError: - pass - - -def __virtual__(): - """ - Only load if pycassa is available and the system is configured - """ - if not HAS_PYCASSA: - return ( - False, - "The cassandra execution module cannot be loaded: pycassa not installed.", - ) - - warn_until_date( - "20240101", - "The cassandra returner is broken and deprecated, and will be removed" - " after {date}. Use the cassandra_cql returner instead", - ) - - if HAS_PYCASSA and salt.utils.path.which("nodetool"): - return "cassandra" - return ( - False, - "The cassandra execution module cannot be loaded: nodetool not found.", - ) - - -def _nodetool(cmd): - """ - Internal cassandra nodetool wrapper. Some functions are not - available via pycassa so we must rely on nodetool. - """ - nodetool = __salt__["config.option"]("cassandra.nodetool") - host = __salt__["config.option"]("cassandra.host") - return __salt__["cmd.run_stdout"]("{} -h {} {}".format(nodetool, host, cmd)) - - -def _sys_mgr(): - """ - Return a pycassa system manager connection object - """ - thrift_port = str(__salt__["config.option"]("cassandra.THRIFT_PORT")) - host = __salt__["config.option"]("cassandra.host") - return SystemManager("{}:{}".format(host, thrift_port)) - - -def compactionstats(): - """ - Return compactionstats info - - CLI Example: - - .. code-block:: bash - - salt '*' cassandra.compactionstats - """ - return _nodetool("compactionstats") - - -def version(): - """ - Return the cassandra version - - CLI Example: - - .. code-block:: bash - - salt '*' cassandra.version - """ - return _nodetool("version") - - -def netstats(): - """ - Return netstats info - - CLI Example: - - .. code-block:: bash - - salt '*' cassandra.netstats - """ - return _nodetool("netstats") - - -def tpstats(): - """ - Return tpstats info - - CLI Example: - - .. code-block:: bash - - salt '*' cassandra.tpstats - """ - return _nodetool("tpstats") - - -def info(): - """ - Return cassandra node info - - CLI Example: - - .. code-block:: bash - - salt '*' cassandra.info - """ - return _nodetool("info") - - -def ring(): - """ - Return cassandra ring info - - CLI Example: - - .. code-block:: bash - - salt '*' cassandra.ring - """ - return _nodetool("ring") - - -def keyspaces(): - """ - Return existing keyspaces - - CLI Example: - - .. code-block:: bash - - salt '*' cassandra.keyspaces - """ - sys = _sys_mgr() - return sys.list_keyspaces() - - -def column_families(keyspace=None): - """ - Return existing column families for all keyspaces - or just the provided one. - - CLI Example: - - .. code-block:: bash - - salt '*' cassandra.column_families - salt '*' cassandra.column_families - """ - sys = _sys_mgr() - ksps = sys.list_keyspaces() - - if keyspace: - if keyspace in ksps: - return list(sys.get_keyspace_column_families(keyspace).keys()) - else: - return None - else: - ret = {} - for kspace in ksps: - ret[kspace] = list(sys.get_keyspace_column_families(kspace).keys()) - - return ret - - -def column_family_definition(keyspace, column_family): - """ - Return a dictionary of column family definitions for the given - keyspace/column_family - - CLI Example: - - .. code-block:: bash - - salt '*' cassandra.column_family_definition - - """ - sys = _sys_mgr() - - try: - return vars(sys.get_keyspace_column_families(keyspace)[column_family]) - except Exception: # pylint: disable=broad-except - log.debug("Invalid Keyspace/CF combination") - return None diff --git a/salt/modules/hashutil.py b/salt/modules/hashutil.py index a4d1de17a63..9e834794e4e 100644 --- a/salt/modules/hashutil.py +++ b/salt/modules/hashutil.py @@ -39,7 +39,7 @@ def digest(instr, checksum="md5"): if hash_func is None: raise salt.exceptions.CommandExecutionError( - "Hash func '{}' is not supported.".format(checksum) + f"Hash func '{checksum}' is not supported." ) return hash_func(instr) @@ -63,9 +63,7 @@ def digest_file(infile, checksum="md5"): salt '*' hashutil.digest_file /path/to/file """ if not __salt__["file.file_exists"](infile): - raise salt.exceptions.CommandExecutionError( - "File path '{}' not found.".format(infile) - ) + raise salt.exceptions.CommandExecutionError(f"File path '{infile}' not found.") with salt.utils.files.fopen(infile, "rb") as f: file_hash = __salt__["hashutil.digest"](f.read(), checksum) @@ -294,4 +292,4 @@ def github_signature(string, shared_secret, challenge_hmac): if isinstance(key, str): key = salt.utils.stringutils.to_bytes(key) hmac_hash = hmac.new(key, msg, getattr(hashlib, hashtype)) - return hmac_hash.hexdigest() == challenge + return hmac.compare_digest(hmac_hash.hexdigest(), challenge) diff --git a/salt/modules/mac_assistive.py b/salt/modules/mac_assistive.py index 742cc9df1f4..7a81fe87a5b 100644 --- a/salt/modules/mac_assistive.py +++ b/salt/modules/mac_assistive.py @@ -72,12 +72,12 @@ def install(app_id, enable=True, tries=3, wait=10): f"Error installing app({app_id}): {exc}" ) elif num_tries < tries: - time.sleep(wait) num_tries += 1 else: raise CommandExecutionError( f"Error installing app({app_id}): {exc}" ) + time.sleep(wait) def installed(app_id): diff --git a/salt/modules/mac_shadow.py b/salt/modules/mac_shadow.py index 0ae1bf1c1bf..1297971fca2 100644 --- a/salt/modules/mac_shadow.py +++ b/salt/modules/mac_shadow.py @@ -53,13 +53,13 @@ def _get_account_policy(name): :raises: CommandExecutionError on user not found or any other unknown error """ - cmd = "pwpolicy -u {} -getpolicy".format(name) + cmd = f"pwpolicy -u {name} -getpolicy" try: ret = salt.utils.mac_utils.execute_return_result(cmd) except CommandExecutionError as exc: - if "Error: user <{}> not found".format(name) in exc.strerror: - raise CommandExecutionError("User not found: {}".format(name)) - raise CommandExecutionError("Unknown error: {}".format(exc.strerror)) + if f"Error: user <{name}> not found" in exc.strerror: + raise CommandExecutionError(f"User not found: {name}") + raise CommandExecutionError(f"Unknown error: {exc.strerror}") try: policy_list = ret.split("\n")[1].split(" ") @@ -85,14 +85,14 @@ def _set_account_policy(name, policy): :raises: CommandExecutionError on user not found or any other unknown error """ - cmd = 'pwpolicy -u {} -setpolicy "{}"'.format(name, policy) + cmd = f'pwpolicy -u {name} -setpolicy "{policy}"' try: return salt.utils.mac_utils.execute_return_success(cmd) except CommandExecutionError as exc: - if "Error: user <{}> not found".format(name) in exc.strerror: - raise CommandExecutionError("User not found: {}".format(name)) - raise CommandExecutionError("Unknown error: {}".format(exc.strerror)) + if f"Error: user <{name}> not found" in exc.strerror: + raise CommandExecutionError(f"User not found: {name}") + raise CommandExecutionError(f"Unknown error: {exc.strerror}") def _get_account_policy_data_value(name, key): @@ -108,13 +108,15 @@ def _get_account_policy_data_value(name, key): :raises: CommandExecutionError on user not found or any other unknown error """ - cmd = "dscl . -readpl /Users/{} accountPolicyData {}".format(name, key) + cmd = f"dscl . -readpl /Users/{name} accountPolicyData {key}" try: ret = salt.utils.mac_utils.execute_return_result(cmd) except CommandExecutionError as exc: if "eDSUnknownNodeName" in exc.strerror: - raise CommandExecutionError("User not found: {}".format(name)) - raise CommandExecutionError("Unknown error: {}".format(exc.strerror)) + raise CommandExecutionError(f"User not found: {name}") + if "eDSUnknownMatchType" in exc.strerror: + raise CommandExecutionError(f"Value not found: {key}") + raise CommandExecutionError(f"Unknown error: {exc.strerror}") return ret @@ -191,7 +193,8 @@ def get_account_created(name): :param str name: The username of the account - :return: The date/time the account was created (yyyy-mm-dd hh:mm:ss) + :return: The date/time the account was created (yyyy-mm-dd hh:mm:ss) or 0 if + the value is not defined :rtype: str :raises: CommandExecutionError on user not found or any other unknown error @@ -202,13 +205,16 @@ def get_account_created(name): salt '*' shadow.get_account_created admin """ - ret = _get_account_policy_data_value(name, "creationTime") + try: + ret = _get_account_policy_data_value(name, "creationTime") + except CommandExecutionError as exc: + if "Value not found" in exc.message: + return "0" + else: + raise unix_timestamp = salt.utils.mac_utils.parse_return(ret) - - date_text = _convert_to_datetime(unix_timestamp) - - return date_text + return _convert_to_datetime(unix_timestamp) def get_last_change(name): @@ -217,7 +223,8 @@ def get_last_change(name): :param str name: The username of the account - :return: The date/time the account was modified (yyyy-mm-dd hh:mm:ss) + :return: The date/time the account was modified (yyyy-mm-dd hh:mm:ss) or 0 + if the value is not defined :rtype: str :raises: CommandExecutionError on user not found or any other unknown error @@ -228,13 +235,16 @@ def get_last_change(name): salt '*' shadow.get_last_change admin """ - ret = _get_account_policy_data_value(name, "passwordLastSetTime") + try: + ret = _get_account_policy_data_value(name, "passwordLastSetTime") + except CommandExecutionError as exc: + if "Value not found" in exc.message: + return "0" + else: + raise unix_timestamp = salt.utils.mac_utils.parse_return(ret) - - date_text = _convert_to_datetime(unix_timestamp) - - return date_text + return _convert_to_datetime(unix_timestamp) def get_login_failed_count(name): @@ -243,8 +253,9 @@ def get_login_failed_count(name): :param str name: The username of the account - :return: The number of failed login attempts - :rtype: int + :return: The number of failed login attempts. 0 may mean there are no failed + login attempts or the value is not defined + :rtype: str :raises: CommandExecutionError on user not found or any other unknown error @@ -254,8 +265,13 @@ def get_login_failed_count(name): salt '*' shadow.get_login_failed_count admin """ - ret = _get_account_policy_data_value(name, "failedLoginCount") - + try: + ret = _get_account_policy_data_value(name, "failedLoginCount") + except CommandExecutionError as exc: + if "Value not found" in exc.message: + return "0" + else: + raise return salt.utils.mac_utils.parse_return(ret) @@ -266,7 +282,7 @@ def get_login_failed_last(name): :param str name: The username of the account :return: The date/time of the last failed login attempt on this account - (yyyy-mm-dd hh:mm:ss) + (yyyy-mm-dd hh:mm:ss) or 0 if the value is not defined :rtype: str :raises: CommandExecutionError on user not found or any other unknown error @@ -277,13 +293,16 @@ def get_login_failed_last(name): salt '*' shadow.get_login_failed_last admin """ - ret = _get_account_policy_data_value(name, "failedLoginTimestamp") + try: + ret = _get_account_policy_data_value(name, "failedLoginTimestamp") + except CommandExecutionError as exc: + if "Value not found" in exc.message: + return "0" + else: + raise unix_timestamp = salt.utils.mac_utils.parse_return(ret) - - date_text = _convert_to_datetime(unix_timestamp) - - return date_text + return _convert_to_datetime(unix_timestamp) def set_maxdays(name, days): @@ -307,7 +326,7 @@ def set_maxdays(name, days): """ minutes = days * 24 * 60 - _set_account_policy(name, "maxMinutesUntilChangePassword={}".format(minutes)) + _set_account_policy(name, f"maxMinutesUntilChangePassword={minutes}") return get_maxdays(name) == days @@ -421,7 +440,7 @@ def set_change(name, date): salt '*' shadow.set_change username 09/21/2016 """ - _set_account_policy(name, "usingExpirationDate=1 expirationDateGMT={}".format(date)) + _set_account_policy(name, f"usingExpirationDate=1 expirationDateGMT={date}") return get_change(name) == date @@ -472,9 +491,7 @@ def set_expire(name, date): salt '*' shadow.set_expire username 07/23/2015 """ - _set_account_policy( - name, "usingHardExpirationDate=1 hardExpireDateGMT={}".format(date) - ) + _set_account_policy(name, f"usingHardExpirationDate=1 hardExpireDateGMT={date}") return get_expire(name) == date @@ -522,16 +539,16 @@ def del_password(name): salt '*' shadow.del_password username """ # This removes the password - cmd = "dscl . -passwd /Users/{} ''".format(name) + cmd = f"dscl . -passwd /Users/{name} ''" try: salt.utils.mac_utils.execute_return_success(cmd) except CommandExecutionError as exc: if "eDSUnknownNodeName" in exc.strerror: - raise CommandExecutionError("User not found: {}".format(name)) - raise CommandExecutionError("Unknown error: {}".format(exc.strerror)) + raise CommandExecutionError(f"User not found: {name}") + raise CommandExecutionError(f"Unknown error: {exc.strerror}") # This is so it looks right in shadow.info - cmd = "dscl . -create /Users/{} Password '*'".format(name) + cmd = f"dscl . -create /Users/{name} Password '*'" salt.utils.mac_utils.execute_return_success(cmd) return info(name)["passwd"] == "*" @@ -558,12 +575,12 @@ def set_password(name, password): salt '*' mac_shadow.set_password macuser macpassword """ - cmd = "dscl . -passwd /Users/{} '{}'".format(name, password) + cmd = f"dscl . -passwd /Users/{name} '{password}'" try: salt.utils.mac_utils.execute_return_success(cmd) except CommandExecutionError as exc: if "eDSUnknownNodeName" in exc.strerror: - raise CommandExecutionError("User not found: {}".format(name)) - raise CommandExecutionError("Unknown error: {}".format(exc.strerror)) + raise CommandExecutionError(f"User not found: {name}") + raise CommandExecutionError(f"Unknown error: {exc.strerror}") return True diff --git a/salt/modules/state.py b/salt/modules/state.py index 72baf47f488..6f23b6f2db3 100644 --- a/salt/modules/state.py +++ b/salt/modules/state.py @@ -145,7 +145,7 @@ def _snapper_pre(opts, jid): snapper_pre = __salt__["snapper.create_snapshot"]( config=__opts__.get("snapper_states_config", "root"), snapshot_type="pre", - description="Salt State run for jid {}".format(jid), + description=f"Salt State run for jid {jid}", __pub_jid=jid, ) except Exception: # pylint: disable=broad-except @@ -164,7 +164,7 @@ def _snapper_post(opts, jid, pre_num): config=__opts__.get("snapper_states_config", "root"), snapshot_type="post", pre_number=pre_num, - description="Salt State run for jid {}".format(jid), + description=f"Salt State run for jid {jid}", __pub_jid=jid, ) except Exception: # pylint: disable=broad-except @@ -587,7 +587,7 @@ def template(tem, queue=None, **kwargs): raise CommandExecutionError("Pillar failed to render", info=errors) if not tem.endswith(".sls"): - tem = "{sls}.sls".format(sls=tem) + tem = f"{tem}.sls" high_state, errors = st_.render_state( tem, kwargs.get("saltenv", ""), "", None, local=True ) @@ -637,7 +637,8 @@ def apply_(mods=None, **kwargs): .. rubric:: APPLYING ALL STATES CONFIGURED IN TOP.SLS (A.K.A. :ref:`HIGHSTATE `) - To apply all configured states, simply run ``state.apply``: + To apply all configured states, simply run ``state.apply`` with no SLS + targets, like so: .. code-block:: bash @@ -882,7 +883,7 @@ def request(mods=None, **kwargs): try: if salt.utils.platform.is_windows(): # Make sure cache file isn't read-only - __salt__["cmd.run"]('attrib -R "{}"'.format(notify_path)) + __salt__["cmd.run"](f'attrib -R "{notify_path}"') with salt.utils.files.fopen(notify_path, "w+b") as fp_: salt.payload.dump(req, fp_) except OSError: @@ -944,7 +945,7 @@ def clear_request(name=None): try: if salt.utils.platform.is_windows(): # Make sure cache file isn't read-only - __salt__["cmd.run"]('attrib -R "{}"'.format(notify_path)) + __salt__["cmd.run"](f'attrib -R "{notify_path}"') with salt.utils.files.fopen(notify_path, "w+b") as fp_: salt.payload.dump(req, fp_) except OSError: @@ -1214,7 +1215,7 @@ def sls( queue=None, sync_mods=None, state_events=None, - **kwargs + **kwargs, ): """ Execute the states in one or more SLS files @@ -1413,7 +1414,7 @@ def sls( for module_type in sync_mods: try: - __salt__["saltutil.sync_{}".format(module_type)](saltenv=opts["saltenv"]) + __salt__[f"saltutil.sync_{module_type}"](saltenv=opts["saltenv"]) except KeyError: log.warning("Invalid custom module type '%s', ignoring", module_type) @@ -2339,10 +2340,10 @@ def pkg(pkg_path, pkg_sum, hash_type, test=None, **kwargs): members = s_pkg.getmembers() for member in members: if salt.utils.stringutils.to_unicode(member.path).startswith( - (os.sep, "..{}".format(os.sep)) + (os.sep, f"..{os.sep}") ): return {} - elif "..{}".format(os.sep) in salt.utils.stringutils.to_unicode(member.path): + elif f"..{os.sep}" in salt.utils.stringutils.to_unicode(member.path): return {} s_pkg.extractall(root) s_pkg.close() @@ -2420,9 +2421,9 @@ def disable(states): _changed = False for _state in states: if _state in _disabled_state_runs: - msg.append("Info: {} state already disabled.".format(_state)) + msg.append(f"Info: {_state} state already disabled.") else: - msg.append("Info: {} state disabled.".format(_state)) + msg.append(f"Info: {_state} state disabled.") _disabled_state_runs.append(_state) _changed = True @@ -2470,9 +2471,9 @@ def enable(states): for _state in states: log.debug("_state %s", _state) if _state not in _disabled_state_runs: - msg.append("Info: {} state already enabled.".format(_state)) + msg.append(f"Info: {_state} state already enabled.") else: - msg.append("Info: {} state enabled.".format(_state)) + msg.append(f"Info: {_state} state enabled.") _disabled_state_runs.remove(_state) _changed = True diff --git a/salt/payload.py b/salt/payload.py index a7dd6902703..46f3e593921 100644 --- a/salt/payload.py +++ b/salt/payload.py @@ -9,7 +9,6 @@ import datetime import gc import logging -import salt.loader.context import salt.transport.frame import salt.utils.immutabletypes as immutabletypes import salt.utils.msgpack diff --git a/salt/pillar/__init__.py b/salt/pillar/__init__.py index 4f83ecb8922..f991f90456f 100644 --- a/salt/pillar/__init__.py +++ b/salt/pillar/__init__.py @@ -571,17 +571,23 @@ class Pillar: # if we didn't pass in functions, lets load them if functions is None: - utils = salt.loader.utils(opts) + utils = salt.loader.utils(opts, file_client=self.client) if opts.get("file_client", "") == "local": - self.functions = salt.loader.minion_mods(opts, utils=utils) + self.functions = salt.loader.minion_mods( + opts, utils=utils, file_client=self.client + ) else: - self.functions = salt.loader.minion_mods(self.opts, utils=utils) + self.functions = salt.loader.minion_mods( + self.opts, utils=utils, file_client=self.client + ) else: self.functions = functions self.opts["minion_id"] = minion_id self.matchers = salt.loader.matchers(self.opts) - self.rend = salt.loader.render(self.opts, self.functions) + self.rend = salt.loader.render( + self.opts, self.functions, self.client, file_client=self.client + ) ext_pillar_opts = copy.deepcopy(self.opts) # Keep the incoming opts ID intact, ie, the master id if "id" in opts: diff --git a/salt/returners/cassandra_return.py b/salt/returners/cassandra_return.py deleted file mode 100644 index ac01a4e46cb..00000000000 --- a/salt/returners/cassandra_return.py +++ /dev/null @@ -1,96 +0,0 @@ -""" -.. warning:: - - The `cassandra` returner is deprecated in favor of the `cassandra_cql` - returner. - -Return data to a Cassandra ColumnFamily - -Here's an example Keyspace / ColumnFamily setup that works with this -returner:: - - create keyspace salt; - use salt; - create column family returns - with key_validation_class='UTF8Type' - and comparator='UTF8Type' - and default_validation_class='UTF8Type'; - -Required python modules: pycassa - - To use the cassandra returner, append '--return cassandra' to the salt command. ex: - - salt '*' test.ping --return cassandra -""" - - -import logging - -import salt.utils.jid -from salt.utils.versions import warn_until_date - -try: - import pycassa # pylint: disable=import-error - - HAS_PYCASSA = True -except ImportError: - HAS_PYCASSA = False - -log = logging.getLogger(__name__) - -__opts__ = { - "cassandra.servers": ["localhost:9160"], - "cassandra.keyspace": "salt", - "cassandra.column_family": "returns", - "cassandra.consistency_level": "ONE", -} - -# Define the module's virtual name -__virtualname__ = "cassandra" - - -def __virtual__(): - if not HAS_PYCASSA: - return False, "Could not import cassandra returner; pycassa is not installed." - warn_until_date( - "20240101", - "The cassandra returner is broken and deprecated, and will be removed" - " after {date}. Use the cassandra_cql returner instead", - ) - return __virtualname__ - - -def returner(ret): - """ - Return data to a Cassandra ColumnFamily - """ - - consistency_level = getattr( - pycassa.ConsistencyLevel, __opts__["cassandra.consistency_level"] - ) - - pool = pycassa.ConnectionPool( - __opts__["cassandra.keyspace"], __opts__["cassandra.servers"] - ) - ccf = pycassa.ColumnFamily( - pool, - __opts__["cassandra.column_family"], - write_consistency_level=consistency_level, - ) - - columns = {"fun": ret["fun"], "id": ret["id"]} - if isinstance(ret["return"], dict): - for key, value in ret["return"].items(): - columns["return.{}".format(key)] = str(value) - else: - columns["return"] = str(ret["return"]) - - log.debug(columns) - ccf.insert(ret["jid"], columns) - - -def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument - """ - Do any work necessary to prepare a JID, including sending a custom id - """ - return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/returners/django_return.py b/salt/returners/django_return.py deleted file mode 100644 index 36386875552..00000000000 --- a/salt/returners/django_return.py +++ /dev/null @@ -1,103 +0,0 @@ -""" -.. deprecated:: 3006.0 - -.. warning:: - - This module has been deprecated and will be removed after January 2024. - -A returner that will inform a Django system that -returns are available using Django's signal system. - -https://docs.djangoproject.com/en/dev/topics/signals/ - -It is up to the Django developer to register necessary -handlers with the signals provided by this returner -and process returns as necessary. - -The easiest way to use signals is to import them from -this returner directly and then use a decorator to register -them. - -An example Django module that registers a function called -'returner_callback' with this module's 'returner' function: - -.. code-block:: python - - import salt.returners.django_return - from django.dispatch import receiver - - @receiver(salt.returners.django_return, sender=returner) - def returner_callback(sender, ret): - print('I received {0} from {1}'.format(ret, sender)) - -""" -# Import Python libraries - -import logging - -# Import Salt libraries -import salt.returners -import salt.utils.jid -from salt.utils.versions import warn_until_date - -log = logging.getLogger(__name__) - -HAS_DJANGO = False - -try: - from django import dispatch # pylint: disable=E0611 - - HAS_DJANGO = True -except ImportError: - HAS_DJANGO = False - -# Define this module's virtual name -__virtualname__ = "django" - - -def __virtual__(): - warn_until_date( - "20240101", - "The django returner is broken and deprecated, and will be removed" - " after {date}.", - ) - if not HAS_DJANGO: - return False, "Could not import django returner; django is not installed." - return True - - -def returner(ret): - """ - Signal a Django server that a return is available - """ - signaled = dispatch.Signal(providing_args=["ret"]).send(sender="returner", ret=ret) - - for signal in signaled: - log.debug( - "Django returner function 'returner' signaled %s which responded with %s", - signal[0], - signal[1], - ) - - -def save_load(jid, load, minions=None): - """ - Save the load to the specified jid - """ - signaled = dispatch.Signal(providing_args=["jid", "load"]).send( - sender="save_load", jid=jid, load=load - ) - - for signal in signaled: - log.debug( - "Django returner function 'save_load' signaled %s which responded with %s", - signal[0], - signal[1], - ) - - -def prep_jid(nocache=False, passed_jid=None): - """ - Do any work necessary to prepare a JID, including sending a custom ID - """ - return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) diff --git a/salt/state.py b/salt/state.py index 95fc5c524b7..50b94e07ca8 100644 --- a/salt/state.py +++ b/salt/state.py @@ -1299,7 +1299,7 @@ class State: Load the modules into the state """ log.info("Loading fresh modules for state activity") - self.utils = salt.loader.utils(self.opts) + self.utils = salt.loader.utils(self.opts, file_client=self.file_client) self.functions = salt.loader.minion_mods( self.opts, self.state_con, @@ -1331,6 +1331,7 @@ class State: self.functions, states=self.states, proxy=self.proxy, + file_client=self.file_client, context=self.state_con, ) diff --git a/salt/states/win_task.py b/salt/states/win_task.py new file mode 100644 index 00000000000..6a83392ebee --- /dev/null +++ b/salt/states/win_task.py @@ -0,0 +1,520 @@ +# https://msdn.microsoft.com/en-us/library/windows/desktop/aa383608(v=vs.85).aspx +""" +State module for adding and removing scheduled tasks using the Windows Task +Scheduler. +""" + +import copy +import logging +import time + +import salt.utils.data +import salt.utils.dateutils +import salt.utils.platform + +try: + import pywintypes + + HAS_PYWIN32 = True +except ImportError: + HAS_PYWIN32 = False + + +ACTION_PARTS = { + "Execute": ["cmd"], + "Email": ["from", "to", "cc", "server"], + "Message": ["title", "message"], +} + +OPTIONAL_ACTION_PARTS = {"start_in": "", "arguments": ""} + +TRIGGER_PARTS = { + "Event": ["subscription"], + "Once": [], + "Daily": ["days_interval"], + "Weekly": ["days_of_week", "weeks_interval"], + "Monthly": ["months_of_year", "days_of_month", "last_day_of_month"], + "MonthlyDay": ["months_of_year", "weeks_of_month", "days_of_week"], + "OnIdle": [], + "OnTaskCreation": [], + "OnBoot": [], + "OnLogon": [], + "OnSessionChange": ["state_change"], +} + +OPTIONAL_TRIGGER_PARTS = { + "trigger_enabled": True, + "start_date": time.strftime("%Y-%m-%d"), + "start_time": time.strftime("%H:%M:%S"), + "end_date": None, + "end_time": "00:00:00", + "random_delay": False, + "repeat_interval": None, + "repeat_duration": None, + "repeat_stop_at_duration_end": False, + "execution_time_limit": "3 days", + "delay": False, +} + +OPTIONAL_CONDITIONS_PARTS = { + "ac_only": True, + "run_if_idle": False, + "run_if_network": False, + "start_when_available": False, +} + +OPTIONAL_SETTINGS_PARTS = { + "allow_demand_start": True, + "delete_after": False, + "execution_time_limit": "3 days", + "force_stop": True, + "multiple_instances": "No New Instance", + "restart_interval": False, + "stop_if_on_batteries": True, + "wake_to_run": False, +} + +TASK_PARTS = { + "actions": {"parts": ACTION_PARTS, "optional": OPTIONAL_ACTION_PARTS}, + "triggers": {"parts": TRIGGER_PARTS, "optional": OPTIONAL_TRIGGER_PARTS}, + "conditions": {"parts": {}, "optional": OPTIONAL_CONDITIONS_PARTS}, + "settings": {"parts": {}, "optional": OPTIONAL_SETTINGS_PARTS}, +} + +log = logging.getLogger(__name__) + +__virtualname__ = "task" + + +def __virtual__(): + """ + Load only on minions running on Windows and with task Module loaded. + """ + if not salt.utils.platform.is_windows(): + return False, "State task: Not a Windows System" + if not HAS_PYWIN32: + return False, "State task: Missing PyWin32 library" + return __virtualname__ + + +def _valid_location(location): + r""" + Test to see if the task location is valid. + + Args: + location (str): task location to check + + Returns: + bool: ``True`` if location is valid, otherwise ``False`` + """ + try: + __salt__["task.list_tasks"](location) + except pywintypes.com_error: + return False + return True + + +def _get_task_state_data(name, location): + r""" + Get the current state of a task in the task scheduler + + Args: + name (str): Task name + location (str): Task location + + Return: + dict: A dictionary containing task configuration information + """ + task_state = {"location_valid": False, "task_found": False, "task_info": {}} + + # if valid location then try to get more info on task + if _valid_location(location): + task_state["location_valid"] = True + task_state["task_found"] = name in __salt__["task.list_tasks"](location) + # if task was found then get actions and triggers info + if task_state["task_found"]: + task_info = __salt__["task.info"](name, location) + task_state["task_info"] = {key: task_info[key] for key in TASK_PARTS} + + return task_state + + +def _get_arguments( + arguments_given, key_arguments, arguments_need_it, optional_arguments +): + """ + Make sure all required arguments are passed + """ + block = {} + # check if key arguments are present + for key in key_arguments: + if key not in arguments_given: + return f"Missing key argument {repr(key)}" + block[key] = arguments_given[key] + + # check if key item valid + if block[key] not in arguments_need_it: + return f"{repr(key)} item {repr(block[key])} is not in key item list {list(arguments_need_it)}" + + # check if key2 present + for key2 in arguments_need_it[block[key]]: + if key2 not in arguments_given: + return f"Missing {key2} argument" + block[key2] = arguments_given[key2] + + # add optional arguments if they're not present + for key in optional_arguments: + if key in arguments_given: + block[key] = arguments_given[key] + else: + block[key] = optional_arguments[key] + + return block + + +def _task_state_prediction_bandage(state): + r""" + A bandage that standardizes date/time formats and adds additional arguments + to a task state. This is so task states can be compared with existing tasks + one the system. + + Args: + state (dict): The dictionary of the current settings for the task + + Returns: + dict: A dictionary with parameters with in the expected format + """ + + # Add "enabled = True" to all triggers + # This is because triggers will add this argument after they're made + if "triggers" in state["task_info"]: + for trigger in state["task_info"]["triggers"]: + trigger["enabled"] = True + + # format dates + for trigger in state["task_info"]["triggers"]: + for key in ["start_date", "end_data"]: + if key in trigger: + # if except is triggered don"t format the date + try: + div = [d for d in ["-", "/"] if d in trigger[key]][0] + part1, part2, part3 = trigger[key].split(div) + if len(part1) == 4: + year, month, day = part1, part2, part3 + else: + month, day, year = part1, part2, part3 + if len(year) != 4: + year = time.strftime("%Y")[:2] + year + + trigger[key] = salt.utils.dateutils.strftime( + f"{year}-{month}-{day}", "%Y-%m-%d" + ) + except IndexError: + pass + except ValueError: + pass + + # format times + for trigger in state["task_info"]["triggers"]: + for key in ["start_time", "end_time"]: + if key in trigger: + try: + trigger[key] = salt.utils.dateutils.strftime( + trigger[key], "%H:%M:%S" + ) + except ValueError: + pass + + return state + + +def _get_task_state_prediction(state, new_task): + r""" + Predicts what the new task will look like after it is applied. Used for + test=True + + Args: + state (dict): A dictionary containing the current task configuration + new_task (dict) A dictionary containing the new task configuration + + Returns: + dict: A dictionary containing predicted result + """ + + new_state = copy.deepcopy(state) + + # if location not valid state can"t be made + if state["location_valid"]: + new_state["task_found"] = True + new_state["task_info"] = { + "actions": [new_task["action"]], + "triggers": [new_task["trigger"]], + "conditions": new_task["conditions"], + "settings": new_task["settings"], + } + + action_keys = set() + trigger_keys = set() + if state["task_found"]: + # get all the arguments used by actions + for action in state["task_info"]["actions"]: + action_keys = action_keys.union(set(action)) + + # get all the arguments used by triggers + for trigger in state["task_info"]["triggers"]: + trigger_keys = trigger_keys.union(set(trigger)) + + # get setup for the for loop below + arguments_filter = [ + [ + new_state["task_info"]["actions"], + action_keys, + TASK_PARTS["actions"]["optional"], + ], + [ + new_state["task_info"]["triggers"], + trigger_keys, + TASK_PARTS["triggers"]["optional"], + ], + ] + + # removes any optional arguments that are equal to the default and is not used by the state + for argument_list, safe_keys, optional_keys in arguments_filter: + for dic in argument_list: + for key in list(dic): + if key not in safe_keys and key in optional_keys: + if dic[key] == optional_keys[key]: + del dic[key] + + # removes add on arguments from triggers + # this is because task info does not give this info + argument_add_on = set(sum((TRIGGER_PARTS[key] for key in TRIGGER_PARTS), [])) + for trigger in new_state["task_info"]["triggers"]: + for key in list(trigger): + if key in argument_add_on: + del trigger[key] + + return _task_state_prediction_bandage(new_state) + + +def present( + name, location="\\", user_name="System", password=None, force=False, **kwargs +): + r""" + Create a new task in the designated location. This function has many keyword + arguments that are not listed here. For additional arguments see: + + .. versionadded:: 3007.0 + + - :py:func:`edit_task` + - :py:func:`add_action` + - :py:func:`add_trigger` + + Args: + name (str): The name of the task. This will be displayed in the task + scheduler. + + location (str): A string value representing the location in which to + create the task. Default is "\\" which is the root for the task + scheduler (C:\Windows\System32\tasks). + + user_name (str): The user account under which to run the task. To + specify the "System" account, use "System". The password will be + ignored. + + password (str): The password to use for authentication. This should set + the task to run whether the user is logged in or not, but is + currently not working. + + force (bool): Overwrite the existing task. + + Returns: + dict: A dictionary containing the results of the state + + CLI Example: + + .. code-block:: YAML + + test_win_task_present: + task.present: + - name: salt + - location: "" + - force: True + - action_type: Execute + - cmd: "del /Q /S C:\\Temp" + - trigger_type: Once + - start_date: 12-1-16 + - start_time: 01:00 + """ + + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + before = _get_task_state_data(name, location) + + # if location not valid the task present will fail + if not before["location_valid"]: + ret["result"] = False + ret["comment"] = f"{repr(location)} is not a valid file location" + return ret + + # split up new task into all its parts + new_task = { + "action": _get_arguments( + kwargs, + ["action_type"], + TASK_PARTS["actions"]["parts"], + TASK_PARTS["actions"]["optional"], + ), + "trigger": _get_arguments( + kwargs, + ["trigger_type"], + TASK_PARTS["triggers"]["parts"], + TASK_PARTS["triggers"]["optional"], + ), + "conditions": _get_arguments( + kwargs, + [], + TASK_PARTS["conditions"]["parts"], + TASK_PARTS["conditions"]["optional"], + ), + "settings": _get_arguments( + kwargs, + [], + TASK_PARTS["settings"]["parts"], + TASK_PARTS["settings"]["optional"], + ), + } + + # if win os is higher than 7 then Email and Message action_type is not supported + try: + if int(__grains__["osversion"].split(".")[0]) >= 8 and new_task["action"][ + "action_type" + ] in ["Email", "Message"]: + log.warning( + "This OS %s does not support Email or Message action_type.", + __grains__["osversion"], + ) + except ValueError: + pass + + for key in new_task: + # if string is returned then an error happened + if isinstance(new_task[key], str): + ret["comment"] = f"{key}: {new_task[key]}" + ret["result"] = None + return ret + + if __opts__["test"]: + # if force is False and task is found then no changes will take place + if not force and before["task_found"]: + ret[ + "comment" + ] = '"force=True" will allow the new task to replace the old one' + ret["result"] = None + log.warning("force=False") + return ret + + after = _get_task_state_prediction(before, new_task) + + ret["changes"] = salt.utils.data.compare_dicts(before, after) + + if ret["changes"]: + ret["result"] = None + return ret + + return ret + + # put all the arguments to kwargs + for key in new_task: + kwargs.update(new_task[key]) + + # make task + result = __salt__["task.create_task"]( + name=name, + location=location, + user_name=user_name, + password=password, + force=force, + **kwargs, + ) + + # if "task.create_task" returns a str then task did not change + if isinstance(result, str): + ret["comment"] = '"force=True" will allow the new task to replace the old one' + ret["result"] = False + log.warning("force=False") + return ret + + after = _get_task_state_data(name, location) + + if after["task_info"]["actions"][0]["action_type"] != kwargs["action_type"]: + ret["comment"] = "failed to make action" + ret["result"] = False + elif after["task_info"]["triggers"][0]["trigger_type"] != kwargs["trigger_type"]: + ret["comment"] = "failed to make trigger" + ret["result"] = False + + ret["changes"] = salt.utils.data.compare_dicts(before, after) + return ret + + +def absent(name, location="\\"): + r""" + Delete a task from the task scheduler. + + .. versionadded:: 3007.0 + + Args: + name (str): The name of the task to delete. + + location (str): A string value representing the location of the task. + Default is "\\" which is the root for the task scheduler + (C:\Windows\System32\tasks). + + Returns: + bool: ``True`` if successful, otherwise ``False`` + + CLI Example: + + .. code-block:: YAML + + test_win_task_absent: + task.absent: + - name: salt + - location: "" + """ + + ret = {"name": name, "changes": {}, "result": True, "comment": ""} + before = _get_task_state_data(name, location) + + # if location not valid the task present will fail + if not before["location_valid"]: + ret["result"] = False + ret["comment"] = f"{repr(location)} is not a valid file location" + return ret + + if __opts__["test"]: + # if task was not found then no changes + if not before["task_found"]: + ret["result"] = True + ret["changes"] = salt.utils.data.compare_dicts(before, before) + else: + # if task was found then changes will happen + ret["result"] = None + ret["changes"] = salt.utils.data.compare_dicts( + before, {"location_valid": True, "task_found": False, "task_info": {}} + ) + return ret + + # if task was found then delete it + if before["task_found"]: + # try to delete task + ret["result"] = __salt__["task.delete_task"](name=name, location=location) + + # if "task.delete_task" returns a str then task was not deleted + if isinstance(ret["result"], str): + ret["result"] = False + + ret["changes"] = salt.utils.data.compare_dicts( + before, _get_task_state_data(name, location) + ) + return ret diff --git a/salt/transport/zeromq.py b/salt/transport/zeromq.py index 73db20586ea..c7e07408ae5 100644 --- a/salt/transport/zeromq.py +++ b/salt/transport/zeromq.py @@ -741,14 +741,16 @@ class AsyncReqMessageClient: try: recv = yield self.socket.recv() except zmq.eventloop.future.CancelledError as exc: - future.set_exception(exc) + if not future.done(): + future.set_exception(exc) return if not future.done(): data = salt.payload.loads(recv) future.set_result(data) except Exception as exc: # pylint: disable=broad-except - future.set_exception(exc) + if not future.done(): + future.set_exception(exc) class ZeroMQSocketMonitor: @@ -884,7 +886,7 @@ class PublishServer(salt.transport.base.DaemonizedPublishServer): ): """ This method represents the Publish Daemon process. It is intended to be - run in a thread or process as it creates and runs an it's own ioloop. + run in a thread or process as it creates and runs its own ioloop. """ ioloop = tornado.ioloop.IOLoop() ioloop.add_callback(self.publisher, publish_payload, ioloop=ioloop) diff --git a/salt/utils/crypt.py b/salt/utils/crypt.py index b89a477a380..e34576c175a 100644 --- a/salt/utils/crypt.py +++ b/salt/utils/crypt.py @@ -5,7 +5,6 @@ import hashlib import logging import os -import salt.loader import salt.utils.files from salt.exceptions import SaltInvocationError @@ -104,13 +103,15 @@ def decrypt( if renderers is None: if opts is None: raise TypeError("opts are required") + + # Avaoid circular import + import salt.loader + renderers = salt.loader.render(opts, {}) rend_func = renderers.get(rend) if rend_func is None: - raise SaltInvocationError( - "Decryption renderer '{}' is not available".format(rend) - ) + raise SaltInvocationError(f"Decryption renderer '{rend}' is not available") return rend_func(data, translate_newlines=translate_newlines) @@ -156,7 +157,7 @@ def pem_finger(path=None, key=None, sum_type="sha256"): for ind, _ in enumerate(pre): if ind % 2: # Is odd - finger += "{}:".format(pre[ind]) + finger += f"{pre[ind]}:" else: finger += pre[ind] return finger.rstrip(":") diff --git a/salt/utils/path.py b/salt/utils/path.py index ed4b4a0f35a..cf3e4cf50bf 100644 --- a/salt/utils/path.py +++ b/salt/utils/path.py @@ -207,7 +207,10 @@ def which(exe=None): # iterate through all extensions to see which one is executable for ext in pathext: pext = p + ext - rp = resolve(pext) + try: + rp = resolve(pext) + except OSError as exc: + continue if is_executable(rp): return p + ext continue @@ -287,7 +290,7 @@ def check_or_die(command): raise CommandNotFoundError("'None' is not a valid command.") if not which(command): - raise CommandNotFoundError("'{}' is not in the path".format(command)) + raise CommandNotFoundError(f"'{command}' is not in the path") def sanitize_win_path(winpath): diff --git a/salt/utils/rsax931.py b/salt/utils/rsax931.py index d84995e3cff..2770743bc5f 100644 --- a/salt/utils/rsax931.py +++ b/salt/utils/rsax931.py @@ -2,7 +2,6 @@ Create and verify ANSI X9.31 RSA signatures using OpenSSL libcrypto """ - import ctypes.util import glob import os @@ -33,16 +32,31 @@ def _find_libcrypto(): elif salt.utils.platform.is_darwin(): # will look for several different location on the system, - # Search in the following order. salts pkg, homebrew, macports, finnally - # system. + # Search in the following order: + # - salt's pkg install location + # - relative to the running python (sys.executable) + # - homebrew + # - macports + # - system libraries + # look in salts pkg install location. lib = glob.glob("/opt/salt/lib/libcrypto.dylib") # look in location salt is running from - # this accounts for running from an unpacked - # onedir file + # this accounts for running from an unpacked onedir file lib = lib or glob.glob("lib/libcrypto.dylib") + # Look in the location relative to the python binary + # Try to account for this being a venv by resolving the path if it is a + # symlink + py_bin = sys.executable + if os.path.islink(py_bin): + py_bin = os.path.realpath(py_bin) + target = os.path.dirname(py_bin) + if os.path.basename(target) == "bin": + target = os.path.dirname(target) + lib = lib or glob.glob(f"{target}/lib/libcrypto.dylib") + # Find library symlinks in Homebrew locations. import salt.modules.mac_brew_pkg as mac_brew diff --git a/salt/utils/templates.py b/salt/utils/templates.py index 5e477207b86..317ccb43460 100644 --- a/salt/utils/templates.py +++ b/salt/utils/templates.py @@ -342,7 +342,6 @@ def render_jinja_tmpl(tmplstr, context, tmplpath=None): saltenv = context["saltenv"] loader = None newline = False - file_client = context.get("fileclient", None) if tmplstr and not isinstance(tmplstr, str): # https://jinja.palletsprojects.com/en/2.11.x/api/#unicode @@ -358,11 +357,13 @@ def render_jinja_tmpl(tmplstr, context, tmplpath=None): if tmplpath: loader = jinja2.FileSystemLoader(os.path.dirname(tmplpath)) else: + from salt.loader.dunder import __file_client__ + loader = salt.utils.jinja.SaltCacheLoader( opts, saltenv, pillar_rend=context.get("_pillar_rend", False), - _file_client=file_client, + _file_client=context.get("fileclient", __file_client__.value()), ) env_args = {"extensions": [], "loader": loader} diff --git a/salt/utils/user.py b/salt/utils/user.py index 5a0aecd0a66..be2abdced2b 100644 --- a/salt/utils/user.py +++ b/salt/utils/user.py @@ -38,6 +38,10 @@ try: except ImportError: HAS_WIN_FUNCTIONS = False +if sys.platform == "win32": + import ctypes.wintypes + + log = logging.getLogger(__name__) diff --git a/tests/conftest.py b/tests/conftest.py index 7c275c4eba6..b25db4172e8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,7 +23,6 @@ import salt import salt._logging import salt._logging.mixins import salt.config -import salt.loader import salt.utils.files import salt.utils.path import salt.utils.platform @@ -338,6 +337,11 @@ def pytest_configure(config): "when called returns `True`. If `skip` is a callable, it should accept a single argument " "'grains', which is the grains dictionary.", ) + config.addinivalue_line( + "markers", + "timeout_unless_on_windows(*args, **kwargs): Apply the 'timeout' marker unless running " + "on Windows.", + ) # "Flag" the slowTest decorator if we're skipping slow tests or not os.environ["SLOW_TESTS"] = str(config.getoption("--run-slow")) @@ -445,7 +449,35 @@ def pytest_collection_modifyitems(config, items): from_filenames_collection_modifyitems(config, items) log.warning("Mofifying collected tests to keep track of fixture usage") + + timeout_marker_tests_paths = ( + str(PYTESTS_DIR / "pkg"), + str(PYTESTS_DIR / "scenarios"), + ) for item in items: + marker = item.get_closest_marker("timeout_unless_on_windows") + if marker is not None: + if not salt.utils.platform.is_windows(): + # Apply the marker since we're not on windows + marker_kwargs = marker.kwargs.copy() + if "func_only" not in marker_kwargs: + # Default to counting only the test execution for the timeouts, ie, + # withough including the fixtures setup time towards the timeout. + marker_kwargs["func_only"] = True + item.add_marker(pytest.mark.timeout(*marker.args, **marker_kwargs)) + else: + if ( + not salt.utils.platform.is_windows() + and not str(pathlib.Path(item.fspath).resolve()).startswith( + timeout_marker_tests_paths + ) + and not item.get_closest_marker("timeout") + ): + # Let's apply the timeout marker on the test, if the marker + # is not already applied + # Default to counting only the test execution for the timeouts, ie, + # withough including the fixtures setup time towards the timeout. + item.add_marker(pytest.mark.timeout(90, func_only=True)) for fixture in item.fixturenames: if fixture not in item._fixtureinfo.name2fixturedefs: continue diff --git a/tests/integration/cli/test_custom_module.py b/tests/integration/cli/test_custom_module.py index 6c048e30cd2..ec8415424e7 100644 --- a/tests/integration/cli/test_custom_module.py +++ b/tests/integration/cli/test_custom_module.py @@ -50,6 +50,7 @@ class SSHCustomModuleTest(SSHCase): self.assertEqual(expected, cmd) @pytest.mark.slow_test + @pytest.mark.timeout(120) def test_ssh_custom_module(self): """ Test custom module work using SSHCase environment @@ -70,7 +71,7 @@ class SSHCustomModuleTest(SSHCase): cmd = self.run_function("state.sls", arg=["custom_module"]) for key in cmd: if not isinstance(cmd, dict) or not isinstance(cmd[key], dict): - raise AssertionError("{} is not a proper state return".format(cmd)) + raise AssertionError(f"{cmd} is not a proper state return") elif not cmd[key]["result"]: raise AssertionError(cmd[key]["comment"]) cmd_ret = cmd[key]["changes"].get("ret", None) diff --git a/tests/integration/loader/test_ext_modules.py b/tests/integration/loader/test_ext_modules.py index bc57f6b5d7e..cd30b4af2e1 100644 --- a/tests/integration/loader/test_ext_modules.py +++ b/tests/integration/loader/test_ext_modules.py @@ -1,14 +1,3 @@ -""" - :codeauthor: Pedro Algarvio (pedro@algarvio.me) - - - integration.loader.ext_modules - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - Test Salt's loader regarding external overrides -""" - - import os import time @@ -24,6 +13,7 @@ class LoaderOverridesTest(ModuleCase): self.run_function("saltutil.sync_modules") @pytest.mark.slow_test + @pytest.mark.timeout_unless_on_windows(120) def test_overridden_internal(self): # To avoid a race condition on Windows, we need to make sure the # `override_test.py` file is present in the _modules directory before diff --git a/tests/integration/modules/test_decorators.py b/tests/integration/modules/test_decorators.py index a3f83cd87c9..5a1ed3817a7 100644 --- a/tests/integration/modules/test_decorators.py +++ b/tests/integration/modules/test_decorators.py @@ -4,6 +4,7 @@ from tests.support.case import ModuleCase @pytest.mark.windows_whitelisted +@pytest.mark.timeout_unless_on_windows(120) class DecoratorTest(ModuleCase): @pytest.mark.slow_test def test_module(self): diff --git a/tests/integration/modules/test_gem.py b/tests/integration/modules/test_gem.py index fca9fbcf7ca..e8c6b031c3b 100644 --- a/tests/integration/modules/test_gem.py +++ b/tests/integration/modules/test_gem.py @@ -18,6 +18,7 @@ def check_status(): return False +@pytest.mark.timeout_unless_on_windows(120) @pytest.mark.skip_if_binaries_missing("gem") @pytest.mark.windows_whitelisted @pytest.mark.destructive_test diff --git a/tests/integration/modules/test_localemod.py b/tests/integration/modules/test_localemod.py index 1fbce18872a..349893e138f 100644 --- a/tests/integration/modules/test_localemod.py +++ b/tests/integration/modules/test_localemod.py @@ -27,6 +27,7 @@ class LocaleModuleTest(ModuleCase): locale = self.run_function("locale.get_locale") self.assertNotIn("Unsupported platform!", locale) + @pytest.mark.timeout(120) @pytest.mark.destructive_test @pytest.mark.slow_test def test_gen_locale(self): diff --git a/tests/integration/modules/test_saltcheck.py b/tests/integration/modules/test_saltcheck.py index 394d80bb8ab..914b046fdd7 100644 --- a/tests/integration/modules/test_saltcheck.py +++ b/tests/integration/modules/test_saltcheck.py @@ -7,6 +7,7 @@ import pytest from tests.support.case import ModuleCase +@pytest.mark.timeout_unless_on_windows(120) class SaltcheckModuleTest(ModuleCase): """ Test the saltcheck module diff --git a/tests/integration/spm/test_build.py b/tests/integration/spm/test_build.py index 653b51864c9..6828db9d753 100644 --- a/tests/integration/spm/test_build.py +++ b/tests/integration/spm/test_build.py @@ -36,6 +36,7 @@ class SPMBuildTest(SPMCase, ModuleCase): @pytest.mark.skip_if_binaries_missing("fallocate") @pytest.mark.slow_test + @pytest.mark.timeout_unless_on_windows(120) def test_spm_build_big_file(self): """ test spm build with a big file diff --git a/tests/integration/ssh/test_state.py b/tests/integration/ssh/test_state.py index a9fd3e7f2d3..825dfbe7162 100644 --- a/tests/integration/ssh/test_state.py +++ b/tests/integration/ssh/test_state.py @@ -18,6 +18,7 @@ log = logging.getLogger(__name__) @pytest.mark.slow_test +@pytest.mark.timeout_unless_on_windows(120) class SSHStateTest(SSHCase): """ testing the state system with salt-ssh @@ -35,7 +36,7 @@ class SSHStateTest(SSHCase): def _check_request(self, empty=False): check = self.run_function("state.check_request", wipe=False) if empty: - self.assertFalse(bool(check), "bool({}) is not False".format(check)) + self.assertFalse(bool(check), f"bool({check}) is not False") else: self._check_dict_ret( ret=check["default"]["test_run"]["local"]["return"], diff --git a/tests/pytests/functional/channel/test_server.py b/tests/pytests/functional/channel/test_server.py index fc35af2241e..05204c16d91 100644 --- a/tests/pytests/functional/channel/test_server.py +++ b/tests/pytests/functional/channel/test_server.py @@ -27,7 +27,8 @@ log = logging.getLogger(__name__) pytestmark = [ pytest.mark.skip_on_spawning_platform( reason="These tests are currently broken on spawning platforms. Need to be rewritten.", - ) + ), + pytest.mark.timeout_unless_on_windows(120), ] diff --git a/tests/pytests/functional/cli/test_batch.py b/tests/pytests/functional/cli/test_batch.py index 7b602c9b825..c82a0ef0a51 100644 --- a/tests/pytests/functional/cli/test_batch.py +++ b/tests/pytests/functional/cli/test_batch.py @@ -140,7 +140,7 @@ class MockSubscriber: }, use_bin_type=True, ) - tag = f"salt/job/{jid}/ret".encode() + tag = f"salt/job/{jid}/ret/{minion_id}".encode() return b"".join([tag, b"\n\n", dumped]) def connect(self, timeout=None): diff --git a/tests/pytests/functional/cli/test_salt_deltaproxy.py b/tests/pytests/functional/cli/test_salt_deltaproxy.py index 9e099b94425..022aa77efcf 100644 --- a/tests/pytests/functional/cli/test_salt_deltaproxy.py +++ b/tests/pytests/functional/cli/test_salt_deltaproxy.py @@ -3,7 +3,6 @@ """ import logging -import os import random import pytest @@ -20,6 +19,7 @@ pytestmark = [ reason="Deltaproxy minions do not currently work on spawning platforms.", ), pytest.mark.core_test, + pytest.mark.timeout_unless_on_windows(320), ] @@ -61,17 +61,6 @@ def proxy_minion_id(salt_master): pytest.helpers.remove_stale_minion_key(salt_master, _proxy_minion_id) -def clear_proxy_minions(salt_master, proxy_minion_id): - for proxy in [proxy_minion_id, "dummy_proxy_one", "dummy_proxy_two"]: - pytest.helpers.remove_stale_minion_key(salt_master, proxy) - - cachefile = os.path.join( - salt_master.config["cachedir"], "{}.cache".format(proxy) - ) - if os.path.exists(cachefile): - os.unlink(cachefile) - - # Hangs on Windows. You can add a timeout to the proxy.run command, but then # it just times out. @pytest.mark.skip_on_windows(reason=PRE_PYTEST_SKIP_REASON) diff --git a/tests/pytests/functional/formulas/test_nginx.py b/tests/pytests/functional/formulas/test_nginx.py index ea6381f7e3b..f1d86091185 100644 --- a/tests/pytests/functional/formulas/test_nginx.py +++ b/tests/pytests/functional/formulas/test_nginx.py @@ -3,6 +3,10 @@ Tests using nginx formula """ import pytest +pytestmark = [ + pytest.mark.timeout_unless_on_windows(120), +] + @pytest.fixture(scope="module") def _formula(saltstack_formula): diff --git a/tests/pytests/functional/loader/test_loader.py b/tests/pytests/functional/loader/test_loader.py index 33b6bd793d3..f34ca2239cf 100644 --- a/tests/pytests/functional/loader/test_loader.py +++ b/tests/pytests/functional/loader/test_loader.py @@ -9,6 +9,7 @@ from tests.support.pytest.helpers import FakeSaltExtension pytestmark = [ # These are slow because they create a virtualenv and install salt in it pytest.mark.slow_test, + pytest.mark.timeout_unless_on_windows(240), ] diff --git a/tests/pytests/functional/modules/state/test_jinja_filters.py b/tests/pytests/functional/modules/state/test_jinja_filters.py index 38135ac967b..0fd44dba74c 100644 --- a/tests/pytests/functional/modules/state/test_jinja_filters.py +++ b/tests/pytests/functional/modules/state/test_jinja_filters.py @@ -1229,6 +1229,7 @@ def filter(request): return _filter +@pytest.mark.timeout_unless_on_windows(120) def test_filter(state, state_tree, filter, grains): filter.check_skip(grains) with filter(state_tree): diff --git a/tests/pytests/functional/modules/test_ansiblegate.py b/tests/pytests/functional/modules/test_ansiblegate.py index 0c51b622709..9a895426a8c 100644 --- a/tests/pytests/functional/modules/test_ansiblegate.py +++ b/tests/pytests/functional/modules/test_ansiblegate.py @@ -10,6 +10,7 @@ pytestmark = [ reason="ansible is not installed", ), pytest.mark.slow_test, + pytest.mark.timeout_unless_on_windows(120), ] diff --git a/tests/pytests/functional/modules/test_mac_brew_pkg.py b/tests/pytests/functional/modules/test_mac_brew_pkg.py index ae6fe9971bf..e1e23606ee8 100644 --- a/tests/pytests/functional/modules/test_mac_brew_pkg.py +++ b/tests/pytests/functional/modules/test_mac_brew_pkg.py @@ -7,6 +7,7 @@ import pytest pytestmark = [ pytest.mark.slow_test, + pytest.mark.timeout(120), pytest.mark.destructive_test, pytest.mark.skip_if_not_root, pytest.mark.skip_unless_on_darwin, diff --git a/tests/pytests/functional/modules/test_mac_pkgutil.py b/tests/pytests/functional/modules/test_mac_pkgutil.py index 02bb2e5641f..397bb895871 100644 --- a/tests/pytests/functional/modules/test_mac_pkgutil.py +++ b/tests/pytests/functional/modules/test_mac_pkgutil.py @@ -8,6 +8,7 @@ import pytest from salt.exceptions import SaltInvocationError pytestmark = [ + pytest.mark.timeout(120), pytest.mark.slow_test, pytest.mark.destructive_test, pytest.mark.skip_if_not_root, diff --git a/tests/pytests/functional/modules/test_mac_softwareupdate.py b/tests/pytests/functional/modules/test_mac_softwareupdate.py index 8cc839f0796..eb5f01550ea 100644 --- a/tests/pytests/functional/modules/test_mac_softwareupdate.py +++ b/tests/pytests/functional/modules/test_mac_softwareupdate.py @@ -8,6 +8,7 @@ from salt.exceptions import SaltInvocationError pytestmark = [ pytest.mark.slow_test, + pytest.mark.timeout(240), pytest.mark.skip_if_binaries_missing("softwareupdate"), pytest.mark.skip_if_not_root, pytest.mark.skip_unless_on_darwin, diff --git a/tests/pytests/functional/modules/test_pkg.py b/tests/pytests/functional/modules/test_pkg.py index aaaba7e0a46..6af7533f2e4 100644 --- a/tests/pytests/functional/modules/test_pkg.py +++ b/tests/pytests/functional/modules/test_pkg.py @@ -3,7 +3,6 @@ import logging import os import re import shutil -import tempfile import time import pytest @@ -15,6 +14,10 @@ import salt.utils.platform log = logging.getLogger(__name__) +pytestmark = [ + pytest.mark.timeout_unless_on_windows(240), +] + @pytest.fixture def ctx(): @@ -22,25 +25,24 @@ def ctx(): @pytest.fixture -def preserve_rhel_yum_conf(): +def _preserve_rhel_yum_conf(tmp_path): # save off current yum.conf cfg_file = "/etc/yum.conf" if not os.path.exists(cfg_file): pytest.skip("Only runs on RedHat.") - tmp_dir = str(tempfile.gettempdir()) - tmp_file = os.path.join(tmp_dir, "yum.conf") + tmp_file = tmp_path / "yum.conf" shutil.copy2(cfg_file, tmp_file) - yield - - # restore saved yum.conf - shutil.copy2(tmp_file, cfg_file) - os.remove(tmp_file) + try: + yield + finally: + # restore saved yum.conf + shutil.copy2(tmp_file, cfg_file) @pytest.fixture -def refresh_db(ctx, grains, modules): +def _refresh_db(ctx, grains, modules): if "refresh" not in ctx: modules.pkg.refresh_db() ctx["refresh"] = True @@ -73,9 +75,10 @@ def test_pkg(grains): return _pkg +@pytest.mark.usefixtures("_refresh_db") @pytest.mark.requires_salt_modules("pkg.list_pkgs") @pytest.mark.slow_test -def test_list(modules, refresh_db): +def test_list(modules): """ verify that packages are installed """ @@ -107,11 +110,12 @@ def test_version_cmp(grains, modules): assert modules.pkg.version_cmp(*gt) == 1 +@pytest.mark.usefixtures("_refresh_db") @pytest.mark.destructive_test @pytest.mark.requires_salt_modules("pkg.mod_repo", "pkg.del_repo", "pkg.get_repo") @pytest.mark.slow_test @pytest.mark.requires_network -def test_mod_del_repo(grains, modules, refresh_db): +def test_mod_del_repo(grains, modules): """ test modifying and deleting a software repository """ @@ -161,7 +165,8 @@ def test_mod_del_repo(grains, modules, refresh_db): @pytest.mark.slow_test -def test_mod_del_repo_multiline_values(modules, refresh_db): +@pytest.mark.usefixtures("_refresh_db") +def test_mod_del_repo_multiline_values(modules): """ test modifying and deleting a software repository defined with multiline values """ @@ -175,7 +180,6 @@ def test_mod_del_repo_multiline_values(modules, refresh_db): expected_get_repo_baseurl = ( "http://my.fake.repo/foo/bar/\nhttp://my.fake.repo.alt/foo/bar/" ) - major_release = int(modules.grains.item("osmajorrelease")["osmajorrelease"]) repo = "fakerepo" name = "Fake repo for RHEL/CentOS/SUSE" baseurl = my_baseurl @@ -224,16 +228,16 @@ def test_which(modules): """ test finding the package owning a file """ - func = "pkg.which" ret = modules.pkg.which("/usr/local/bin/salt-call") assert len(ret) != 0 +@pytest.mark.usefixtures("_refresh_db") @pytest.mark.destructive_test @pytest.mark.requires_salt_modules("pkg.version", "pkg.install", "pkg.remove") @pytest.mark.slow_test @pytest.mark.requires_network -def test_install_remove(modules, test_pkg, refresh_db): +def test_install_remove(modules, test_pkg): """ successfully install and uninstall a package """ @@ -258,6 +262,7 @@ def test_install_remove(modules, test_pkg, refresh_db): test_remove() +@pytest.mark.usefixtures("_refresh_db") @pytest.mark.destructive_test @pytest.mark.skip_on_photonos( reason="package hold/unhold unsupported on Photon OS", @@ -273,7 +278,7 @@ def test_install_remove(modules, test_pkg, refresh_db): @pytest.mark.slow_test @pytest.mark.requires_network @pytest.mark.requires_salt_states("pkg.installed") -def test_hold_unhold(grains, modules, states, test_pkg, refresh_db): +def test_hold_unhold(grains, modules, states, test_pkg): """ test holding and unholding a package """ @@ -315,11 +320,12 @@ def test_hold_unhold(grains, modules, states, test_pkg, refresh_db): assert ret.result is True +@pytest.mark.usefixtures("_refresh_db") @pytest.mark.destructive_test @pytest.mark.requires_salt_modules("pkg.refresh_db") @pytest.mark.slow_test @pytest.mark.requires_network -def test_refresh_db(grains, tmp_path, minion_opts, refresh_db): +def test_refresh_db(grains, minion_opts): """ test refreshing the package database """ @@ -344,9 +350,10 @@ def test_refresh_db(grains, tmp_path, minion_opts, refresh_db): assert os.path.isfile(rtag) is False +@pytest.mark.usefixtures("_refresh_db") @pytest.mark.requires_salt_modules("pkg.info_installed") @pytest.mark.slow_test -def test_pkg_info(grains, modules, test_pkg, refresh_db): +def test_pkg_info(grains, modules, test_pkg): """ Test returning useful information on Ubuntu systems. """ @@ -371,6 +378,7 @@ def test_pkg_info(grains, modules, test_pkg, refresh_db): assert test_pkg in keys +@pytest.mark.usefixtures("_refresh_db") @pytest.mark.skipif(True, reason="Temporary Skip - Causes centos 8 test to fail") @pytest.mark.destructive_test @pytest.mark.requires_salt_modules( @@ -382,7 +390,7 @@ def test_pkg_info(grains, modules, test_pkg, refresh_db): ) @pytest.mark.slow_test @pytest.mark.requires_network -def test_pkg_upgrade_has_pending_upgrades(grains, modules, test_pkg, refresh_db): +def test_pkg_upgrade_has_pending_upgrades(grains, modules): """ Test running a system upgrade when there are packages that need upgrading """ @@ -450,6 +458,7 @@ def test_pkg_upgrade_has_pending_upgrades(grains, modules, test_pkg, refresh_db) assert ret != {} +@pytest.mark.usefixtures("_refresh_db") @pytest.mark.destructive_test @pytest.mark.skip_on_darwin( reason="The jenkins user is equivalent to root on mac, causing the test to be unrunnable" @@ -457,7 +466,7 @@ def test_pkg_upgrade_has_pending_upgrades(grains, modules, test_pkg, refresh_db) @pytest.mark.requires_salt_modules("pkg.remove", "pkg.latest_version") @pytest.mark.slow_test @pytest.mark.requires_salt_states("pkg.removed") -def test_pkg_latest_version(grains, modules, states, test_pkg, refresh_db): +def test_pkg_latest_version(grains, modules, states, test_pkg): """ Check that pkg.latest_version returns the latest version of the uninstalled package. The package is not installed. Only the package version is checked. @@ -493,10 +502,11 @@ def test_pkg_latest_version(grains, modules, states, test_pkg, refresh_db): assert pkg_latest in cmd_pkg +@pytest.mark.usefixtures("_preserve_rhel_yum_conf") @pytest.mark.destructive_test @pytest.mark.requires_salt_modules("pkg.list_repos") @pytest.mark.slow_test -def test_list_repos_duplicate_entries(preserve_rhel_yum_conf, grains, modules): +def test_list_repos_duplicate_entries(grains, modules): """ test duplicate entries in /etc/yum.conf @@ -527,13 +537,13 @@ def test_list_repos_duplicate_entries(preserve_rhel_yum_conf, grains, modules): # test explicitly strict_config expected = "While reading from '/etc/yum.conf' [line 8]: option 'http_caching' in section 'main' already exists" with pytest.raises(configparser.DuplicateOptionError) as exc_info: - result = modules.pkg.list_repos(strict_config=True) - assert f"{exc_info.value}" == expected + modules.pkg.list_repos(strict_config=True) + assert str(exc_info.value) == expected # test implicitly strict_config with pytest.raises(configparser.DuplicateOptionError) as exc_info: - result = modules.pkg.list_repos() - assert f"{exc_info.value}" == expected + modules.pkg.list_repos() + assert str(exc_info.value) == expected @pytest.mark.destructive_test diff --git a/tests/pytests/functional/states/pkgrepo/test_debian.py b/tests/pytests/functional/states/pkgrepo/test_debian.py index b8c8344c095..5452ecac1c4 100644 --- a/tests/pytests/functional/states/pkgrepo/test_debian.py +++ b/tests/pytests/functional/states/pkgrepo/test_debian.py @@ -6,7 +6,6 @@ import shutil import sys from sysconfig import get_path -import _pytest._version import attr import pytest @@ -15,12 +14,10 @@ import salt.utils.files from tests.conftest import CODE_DIR from tests.support.mock import MagicMock, patch -PYTEST_GE_7 = getattr(_pytest._version, "version_tuple", (-1, -1)) >= (7, 0) - - log = logging.getLogger(__name__) pytestmark = [ + pytest.mark.timeout_unless_on_windows(120), pytest.mark.destructive_test, pytest.mark.skip_if_not_root, pytest.mark.slow_test, @@ -30,11 +27,8 @@ pytestmark = [ @pytest.fixture def pkgrepo(states, grains): if grains["os_family"] != "Debian": - exc_kwargs = {} - if PYTEST_GE_7: - exc_kwargs["_use_item_location"] = True raise pytest.skip.Exception( - "Test only for debian based platforms", **exc_kwargs + "Test only for debian based platforms", _use_item_location=True ) return states.pkgrepo @@ -102,12 +96,9 @@ def system_aptsources_ids(value): def system_aptsources(request, grains): sys_modules = list(sys.modules) copied_paths = [] - exc_kwargs = {} - if PYTEST_GE_7: - exc_kwargs["_use_item_location"] = True if grains["os_family"] != "Debian": raise pytest.skip.Exception( - "Test only for debian based platforms", **exc_kwargs + "Test only for debian based platforms", _use_item_location=True ) try: try: @@ -117,7 +108,7 @@ def system_aptsources(request, grains): raise pytest.skip.Exception( "This test is meant to run without the system aptsources package, but it's " "available from '{}'.".format(sourceslist.__file__), - **exc_kwargs, + _use_item_location=True, ) else: # Run the test @@ -162,7 +153,8 @@ def system_aptsources(request, grains): shutil.copyfile(src, dst) if not copied_paths: raise pytest.skip.Exception( - "aptsources.sourceslist python module not found", **exc_kwargs + "aptsources.sourceslist python module not found", + _use_item_location=True, ) # Run the test yield request.param diff --git a/tests/pytests/functional/states/test_docker_container.py b/tests/pytests/functional/states/test_docker_container.py index 2267399891e..10e6509466d 100644 --- a/tests/pytests/functional/states/test_docker_container.py +++ b/tests/pytests/functional/states/test_docker_container.py @@ -29,6 +29,7 @@ pytestmark = [ pytest.mark.skip_if_binaries_missing( "docker", "dockerd", reason="Docker not installed" ), + pytest.mark.timeout_unless_on_windows(240), ] IPV6_ENABLED = bool(salt.utils.network.ip_addrs6(include_loopback=True)) @@ -62,7 +63,7 @@ class Network: @ip_arg.default def _ip_arg(self): - return "ipv{}_address".format(self.net.version) + return f"ipv{self.net.version}_address" @enable_ipv6.default def _enable_ipv6(self): @@ -70,12 +71,13 @@ class Network: @staticmethod def arg_map(arg_name): - return { + nwmap = { "ipv4_address": "IPv4Address", "ipv6_address": "IPv6Address", "links": "Links", "aliases": "Aliases", - }[arg_name] + } + return nwmap[arg_name] @property def compressed_subnet(self): @@ -226,7 +228,7 @@ def test_running_with_no_predefined_volume( ret = docker_container.running( name=container_name, image=image, - binds="{}:/foo".format(tmp_path), + binds=f"{tmp_path}:/foo", shutdown_timeout=1, ) assert ret.result is True @@ -566,7 +568,7 @@ def test_absent_with_stopped_container( # Nothing should have changed assert ret.changes == {} # Ensure that the comment field says the container does not exist - assert ret.comment == "Container '{}' does not exist".format(container_name) + assert ret.comment == f"Container '{container_name}' does not exist" @pytest.mark.slow_test @@ -605,7 +607,7 @@ def test_absent_with_running_container(docker_container, container_name, image): # Check that we have a removed container ID in the changes dict assert "removed" in ret.changes # The comment should mention that the container was removed - assert ret.comment == "Forcibly removed container '{}'".format(container_name) + assert ret.comment == f"Forcibly removed container '{container_name}'" @pytest.mark.slow_test @@ -735,7 +737,7 @@ def test_running_networks( ) log.error(msg) log.error("Connected networks: %s", connected_networks) - pytest.fail("{}. See log for more information.".format(msg)) + pytest.fail(f"{msg}. See log for more information.") # Check that container continued running and didn't immediately exit assert inspect_result["State"]["Running"] @@ -758,12 +760,10 @@ def test_running_networks( } assert ret.changes == expected - expected = [ - "Container '{}' is already configured as specified.".format(container_name) - ] + expected = [f"Container '{container_name}' is already configured as specified."] expected.extend( [ - "Reconnected to network '{}' with updated configuration.".format(x.name) + f"Reconnected to network '{x.name}' with updated configuration." for x in sorted(networks.nets, key=lambda y: y.name) ] ) @@ -884,7 +884,7 @@ def test_running_explicit_networks( net_changes = ret.changes["container"]["Networks"] assert ( - "Container '{}' is already configured as specified.".format(container_name) + f"Container '{container_name}' is already configured as specified." in ret.comment ) @@ -893,15 +893,13 @@ def test_running_explicit_networks( ]["Networks"] for default_network in default_networks: - assert ( - "Disconnected from network '{}'.".format(default_network) in ret.comment - ) + assert f"Disconnected from network '{default_network}'." in ret.comment assert default_network in net_changes # We've tested that the state return is correct, but let's be extra # paranoid and check the actual connected networks. assert default_network not in updated_networks - assert "Connected to network '{}'.".format(net.name) in ret.comment + assert f"Connected to network '{net.name}'." in ret.comment def test_run_with_onlyif(docker_container, container_name, image, modules): @@ -1021,7 +1019,7 @@ def test_run_with_creates( ) assert ret.result is True assert not ret.changes - assert ret.comment == "{} exists".format(good_file1) + assert ret.comment == f"{good_file1} exists" finally: try: modules.docker.rm(container_name, force=True) diff --git a/tests/pytests/functional/states/test_npm.py b/tests/pytests/functional/states/test_npm.py index 2899b7985a1..81dd00f5544 100644 --- a/tests/pytests/functional/states/test_npm.py +++ b/tests/pytests/functional/states/test_npm.py @@ -23,7 +23,7 @@ def install_npm(states): # Just name the thing we're looking for states.npm # pylint: disable=pointless-statement except (CommandExecutionError, AttributeError, AssertionError) as exc: - pytest.skip("Unable to install npm - {}".format(exc)) + pytest.skip(f"Unable to install npm - {exc}") @pytest.fixture(scope="module") @@ -55,6 +55,7 @@ def npm(states, modules, apply_gitconfig_workaround): @pytest.mark.skip_if_not_root +@pytest.mark.timeout_unless_on_windows(120) def test_removed_installed_cycle(npm, modules): project_version = "pm2@5.1.0" success = modules.npm.uninstall("pm2") diff --git a/tests/pytests/functional/states/test_pkg.py b/tests/pytests/functional/states/test_pkg.py index 2e3a7aa6af6..13e316c7fae 100644 --- a/tests/pytests/functional/states/test_pkg.py +++ b/tests/pytests/functional/states/test_pkg.py @@ -19,6 +19,7 @@ pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_not_root, pytest.mark.destructive_test, + pytest.mark.timeout_unless_on_windows(240), ] diff --git a/tests/pytests/functional/test_crypt.py b/tests/pytests/functional/test_crypt.py new file mode 100644 index 00000000000..0c8e90e82d6 --- /dev/null +++ b/tests/pytests/functional/test_crypt.py @@ -0,0 +1,12 @@ +import pathlib + +import pytest + +import salt.crypt + + +@pytest.mark.windows_whitelisted +def test_generated_keys(tmp_path): + priv = salt.crypt.gen_keys(tmp_path, "aaa", 2048) + assert "\r" not in pathlib.Path(priv).read_text() + assert "\r" not in pathlib.Path(priv.replace(".pem", ".pub")).read_text() diff --git a/tests/pytests/functional/test_fileclient_reuse.py b/tests/pytests/functional/test_fileclient_reuse.py new file mode 100644 index 00000000000..87f6f706663 --- /dev/null +++ b/tests/pytests/functional/test_fileclient_reuse.py @@ -0,0 +1,114 @@ +import pytest + +import salt.loader +import salt.pillar +import salt.state +import salt.utils.cache +import salt.utils.jinja +from tests.support.mock import patch + + +@pytest.fixture +def mock_cached_loader(): + """ + Mock the SaltCacheLoader + + The mock keeps track of the number of + instantiations and the most recent args and kwargs. + """ + + class CacheLoader(salt.utils.jinja.SaltCacheLoader): + args = [] + kwargs = {} + calls = 0 + + def __init__(self, *args, **kwargs): + self.__class__.calls += 1 + self.__class__.args = args + self.__class__.kwargs = kwargs + super().__init__(*args, **kwargs) + + with patch("salt.utils.jinja.SaltCacheLoader", CacheLoader): + yield CacheLoader + + +def test_pillar_tops(temp_salt_master, temp_salt_minion, tmp_path, mock_cached_loader): + """ + pillar fileclient is used while rendering pillar tops + """ + tops = """ + base: + "*": + - test_pillar + """ + pillarsls = """ + foo: bar + """ + opts = temp_salt_master.config.copy() + + with temp_salt_master.pillar_tree.base.temp_file("top.sls", tops): + with temp_salt_master.pillar_tree.base.temp_file("test_pillar.sls", pillarsls): + grains = salt.loader.grains(opts) + pillar = salt.pillar.Pillar( + opts, + grains, + temp_salt_minion.id, + "base", + ) + pillar.get_tops() + assert mock_cached_loader.calls == 1 + assert "_file_client" in mock_cached_loader.kwargs + assert mock_cached_loader.kwargs["_file_client"] == pillar.client + + +def test_pillar_render( + temp_salt_master, temp_salt_minion, tmp_path, mock_cached_loader +): + """ + pillar fileclient is used while rendering jinja pillar + """ + tops = """ + base: + "*": + - test_pillar + """ + pillarsls = """ + foo: bar + """ + opts = temp_salt_master.config.copy() + + with temp_salt_master.pillar_tree.base.temp_file("top.sls", tops): + with temp_salt_master.pillar_tree.base.temp_file("test_pillar.sls", pillarsls): + grains = salt.loader.grains(opts) + pillar = salt.pillar.Pillar( + opts, + grains, + temp_salt_minion.id, + "base", + ) + pdata = pillar.render_pillar({"base": ["test_pillar"]}) + assert pdata == ({"foo": "bar"}, []) + assert mock_cached_loader.calls == 1 + assert "_file_client" in mock_cached_loader.kwargs + assert mock_cached_loader.kwargs["_file_client"] == pillar.client + + +def test_highstate(temp_salt_master, temp_salt_minion, tmp_path, mock_cached_loader): + """ + pillar fileclient is used while rendering pillar tops + """ + statesls = """ + test_state: + cmd.run: + - name: echos foo + """ + opts = temp_salt_master.config.copy() + + with temp_salt_master.state_tree.base.temp_file("test_state.sls", statesls): + highstate = salt.state.HighState( + opts, + ) + a = highstate.render_highstate({"base": ["test_state"]}) + assert mock_cached_loader.calls == 1 + assert "_file_client" in mock_cached_loader.kwargs + assert mock_cached_loader.kwargs["_file_client"] == highstate.client diff --git a/tests/pytests/functional/test_version.py b/tests/pytests/functional/test_version.py index dfa8850557e..b0e40dc3666 100644 --- a/tests/pytests/functional/test_version.py +++ b/tests/pytests/functional/test_version.py @@ -9,6 +9,7 @@ from tests.support.pytest.helpers import FakeSaltExtension pytestmark = [ # These are slow because they create a virtualenv and install salt in it pytest.mark.slow_test, + pytest.mark.timeout(120), ] log = logging.getLogger(__name__) diff --git a/tests/pytests/integration/_logging/test_logging.py b/tests/pytests/integration/_logging/test_logging.py index a0fa779308b..8e38f55b38d 100644 --- a/tests/pytests/integration/_logging/test_logging.py +++ b/tests/pytests/integration/_logging/test_logging.py @@ -48,7 +48,7 @@ def log_nameToLevel(name): def test_lowest_log_level(): ret = log_impl.get_lowest_log_level() - assert ret is None + assert ret is not None log_impl.set_lowest_log_level(log_nameToLevel("DEBUG")) ret = log_impl.get_lowest_log_level() diff --git a/tests/pytests/integration/cli/test_matcher.py b/tests/pytests/integration/cli/test_matcher.py index f6b18b51c42..06a44c16df0 100644 --- a/tests/pytests/integration/cli/test_matcher.py +++ b/tests/pytests/integration/cli/test_matcher.py @@ -7,6 +7,7 @@ import salt.defaults.exitcodes pytestmark = [ pytest.mark.core_test, pytest.mark.windows_whitelisted, + pytest.mark.timeout_unless_on_windows(120), ] diff --git a/tests/pytests/integration/cli/test_salt_deltaproxy.py b/tests/pytests/integration/cli/test_salt_deltaproxy.py index 6ada4437595..3457a972aec 100644 --- a/tests/pytests/integration/cli/test_salt_deltaproxy.py +++ b/tests/pytests/integration/cli/test_salt_deltaproxy.py @@ -1,9 +1,8 @@ """ :codeauthor: Gareth J. Greenaway (ggreenaway@vmware.com) """ - import logging -import os +import random import pytest from pytestshellutils.exceptions import FactoryNotStarted @@ -20,11 +19,12 @@ pytestmark = [ reason="Deltaproxy minions do not currently work on spawning platforms.", ), pytest.mark.core_test, + pytest.mark.timeout_unless_on_windows(400), ] @pytest.fixture(scope="package", autouse=True) -def skip_on_tcp_transport(request): +def _skip_on_tcp_transport(request): if request.config.getoption("--transport") == "tcp": pytest.skip("Deltaproxy under the TPC transport is not working. See #61367") @@ -40,15 +40,6 @@ def proxy_minion_id(salt_master): pytest.helpers.remove_stale_minion_key(salt_master, _proxy_minion_id) -def clear_proxy_minions(salt_master, proxy_minion_id): - for proxy in [proxy_minion_id, "dummy_proxy_one", "dummy_proxy_two"]: - pytest.helpers.remove_stale_minion_key(salt_master, proxy) - - cachefile = os.path.join(salt_master.config["cachedir"], f"{proxy}.cache") - if os.path.exists(cachefile): - os.unlink(cachefile) - - def test_exit_status_no_proxyid(salt_master, proxy_minion_id): """ Ensure correct exit status when --proxyid argument is missing. @@ -191,7 +182,7 @@ def test_exit_status_correct_usage( proxy_minion_id, defaults=config_defaults, extra_cli_arguments_after_first_start_failure=["--log-level=info"], - start_timeout=240, + start_timeout=320, ) for minion_id in (proxy_minion_id, proxy_one, proxy_two): @@ -294,7 +285,7 @@ def test_missing_pillar_file( proxy_minion_id, defaults=config_defaults, extra_cli_arguments_after_first_start_failure=["--log-level=info"], - start_timeout=240, + start_timeout=320, ) for minion_id in (proxy_minion_id, proxy_one, proxy_two): @@ -418,7 +409,7 @@ def test_invalid_connection( proxy_minion_id, defaults=config_defaults, extra_cli_arguments_after_first_start_failure=["--log-level=info"], - start_timeout=240, + start_timeout=320, ) for minion_id in ( @@ -462,7 +453,6 @@ def test_custom_proxy_module( salt_cli, proxy_minion_id, parallel_startup, - integration_files_dir, ): """ Ensure the salt-proxy control proxy starts and @@ -548,7 +538,7 @@ def ping(): proxy_minion_id, defaults=config_defaults, extra_cli_arguments_after_first_start_failure=["--log-level=info"], - start_timeout=240, + start_timeout=320, ) for minion_id in (proxy_minion_id, proxy_one, proxy_two): @@ -596,7 +586,6 @@ def test_custom_proxy_module_raise_exception( salt_cli, proxy_minion_id, parallel_startup, - integration_files_dir, ): """ Ensure the salt-proxy control proxy starts and @@ -682,7 +671,7 @@ def ping(): proxy_minion_id, defaults=config_defaults, extra_cli_arguments_after_first_start_failure=["--log-level=info"], - start_timeout=240, + start_timeout=320, ) for minion_id in (proxy_minion_id, proxy_one, proxy_two): @@ -717,3 +706,144 @@ def ping(): # Terminate the proxy minion ret = factory.terminate() assert ret.returncode == salt.defaults.exitcodes.EX_OK, ret + + +# Hangs on Windows. You can add a timeout to the proxy.run command, but then +# it just times out. +@pytest.mark.skip_on_windows(reason=PRE_PYTEST_SKIP_REASON) +@pytest.mark.parametrize( + "parallel_startup", + [True, False], + ids=["parallel_startup=True", "parallel_startup=False"], +) +def test_exit_status_correct_usage_large_number_of_minions( + salt_master, + salt_cli, + proxy_minion_id, + parallel_startup, +): + """ + Ensure the salt-proxy control proxy starts and + is able to respond to test.ping, additionally ensure that + the proxies being controlled also respond to test.ping. + + Finally ensure correct exit status when salt-proxy exits correctly. + + Skip on Windows because daemonization not supported + """ + + config_defaults = { + "metaproxy": "deltaproxy", + } + sub_proxies = [ + "proxy_one", + "proxy_two", + "proxy_three", + "proxy_four", + "proxy_five", + "proxy_six", + "proxy_seven", + "proxy_eight", + "proxy_nine", + "proxy_ten", + "proxy_eleven", + "proxy_twelve", + "proxy_thirteen", + "proxy_fourteen", + "proxy_fifteen", + "proxy_sixteen", + "proxy_seventeen", + "proxy_eighteen", + "proxy_nineteen", + "proxy_twenty", + "proxy_twenty_one", + "proxy_twenty_two", + "proxy_twenty_three", + "proxy_twenty_four", + "proxy_twenty_five", + "proxy_twenty_six", + "proxy_twenty_seven", + "proxy_twenty_eight", + "proxy_twenty_nine", + "proxy_thirty", + "proxy_thirty_one", + "proxy_thirty_two", + ] + + top_file = """ + base: + {control}: + - controlproxy + """.format( + control=proxy_minion_id, + ) + controlproxy_pillar_file = f""" + proxy: + proxytype: deltaproxy + parallel_startup: {parallel_startup} + ids: + """ + + dummy_proxy_pillar_file = """ + proxy: + proxytype: dummy + """ + + for minion_id in sub_proxies: + top_file += f""" + {minion_id}: + - dummy""" + + controlproxy_pillar_file += f""" + - {minion_id} + """ + + top_tempfile = salt_master.pillar_tree.base.temp_file("top.sls", top_file) + controlproxy_tempfile = salt_master.pillar_tree.base.temp_file( + "controlproxy.sls", controlproxy_pillar_file + ) + dummy_proxy_tempfile = salt_master.pillar_tree.base.temp_file( + "dummy.sls", + dummy_proxy_pillar_file, + ) + with top_tempfile, controlproxy_tempfile, dummy_proxy_tempfile: + + factory = salt_master.salt_proxy_minion_daemon( + proxy_minion_id, + defaults=config_defaults, + extra_cli_arguments_after_first_start_failure=["--log-level=info"], + start_timeout=320, + ) + + for minion_id in [proxy_minion_id] + sub_proxies: + factory.before_start( + pytest.helpers.remove_stale_proxy_minion_cache_file, + factory, + minion_id, + ) + factory.after_terminate( + pytest.helpers.remove_stale_minion_key, salt_master, minion_id + ) + factory.after_terminate( + pytest.helpers.remove_stale_proxy_minion_cache_file, + factory, + minion_id, + ) + + with factory.started(): + assert factory.is_running() + + # Let's issue a ping the control proxy + ret = salt_cli.run("test.ping", minion_tgt=proxy_minion_id) + assert ret.returncode == 0 + assert ret.data is True + + for minion_id in random.sample(sub_proxies, 4): + # Let's issue a ping to one of the controlled proxies + ret = salt_cli.run("test.ping", minion_tgt=minion_id) + assert ret.returncode == 0 + assert ret.data is True + + # Terminate the proxy minion + ret = factory.terminate() + assert ret.returncode == salt.defaults.exitcodes.EX_OK, ret diff --git a/tests/pytests/integration/cli/test_syndic_eauth.py b/tests/pytests/integration/cli/test_syndic_eauth.py index 3fa4033ec5a..a37127c3949 100644 --- a/tests/pytests/integration/cli/test_syndic_eauth.py +++ b/tests/pytests/integration/cli/test_syndic_eauth.py @@ -1,6 +1,5 @@ import json -import pathlib -import tempfile +import logging import time import pytest @@ -9,16 +8,18 @@ from tests.conftest import CODE_DIR docker = pytest.importorskip("docker") +log = logging.getLogger(__name__) pytestmark = [ pytest.mark.core_test, + pytest.mark.timeout_unless_on_windows(600), ] def json_output_to_dict(output): - """ + r""" Convert ``salt ... --out=json`` Syndic return to a dictionary. Since the - --out=json will return several JSON outputs, e.g. {...}\\n{...}, we have to + --out=json will return several JSON outputs, e.g. {...}\n{...}, we have to parse that output individually. """ output = output or "" @@ -35,20 +36,6 @@ def json_output_to_dict(output): return results -def accept_keys(container, required_minions): - failure_time = time.time() + 20 - while time.time() < failure_time: - container.run("salt-key -Ay") - res = container.run("salt-key -L --out=json") - if ( - isinstance(res.data, dict) - and set(res.data.get("minions")) == required_minions - ): - break - else: - pytest.skip(f"{container} unable to accept keys for {required_minions}") - - @pytest.fixture(scope="module") def syndic_network(): try: @@ -72,56 +59,49 @@ def syndic_network(): network.remove() -@pytest.fixture(scope="module") -def source_path(): - return str(CODE_DIR / "salt") - - @pytest.fixture(scope="module") def container_image_name(): - return "ghcr.io/saltstack/salt-ci-containers/salt:3005" + return "ghcr.io/saltstack/salt-ci-containers/salt:3006" @pytest.fixture(scope="module") def container_python_version(): - return "3.7" + return "3.10" @pytest.fixture(scope="module") -def config(source_path): - # 3.10>= will allow the below line - # with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as tmp_path: - with tempfile.TemporaryDirectory() as tmp_path: - tmp_path = pathlib.Path(tmp_path) - master_dir = tmp_path / "master" - minion_dir = tmp_path / "minion" - syndic_a_dir = tmp_path / "syndic_a" - syndic_b_dir = tmp_path / "syndic_b" - minion_a1_dir = tmp_path / "minion_a1" - minion_a2_dir = tmp_path / "minion_a2" - minion_b1_dir = tmp_path / "minion_b1" - minion_b2_dir = tmp_path / "minion_b2" +def config(tmp_path_factory): + tmp_path = tmp_path_factory.mktemp("eauth") + master_dir = tmp_path / "master" + minion_dir = tmp_path / "minion" + syndic_a_dir = tmp_path / "syndic_a" + syndic_b_dir = tmp_path / "syndic_b" + minion_a1_dir = tmp_path / "minion_a1" + minion_a2_dir = tmp_path / "minion_a2" + minion_b1_dir = tmp_path / "minion_b1" + minion_b2_dir = tmp_path / "minion_b2" - for dir_ in ( - master_dir, - minion_dir, - syndic_a_dir, - syndic_b_dir, - minion_a1_dir, - minion_a2_dir, - minion_b1_dir, - minion_b2_dir, - ): - dir_.mkdir(parents=True, exist_ok=True) - (dir_ / "master.d").mkdir(exist_ok=True) - # minion.d is probably needed to prevent errors on tempdir cleanup - (dir_ / "minion.d").mkdir(exist_ok=True) - (dir_ / "pki").mkdir(exist_ok=True) - (master_dir / "master.d").mkdir(exist_ok=True) + for dir_ in ( + master_dir, + minion_dir, + syndic_a_dir, + syndic_b_dir, + minion_a1_dir, + minion_a2_dir, + minion_b1_dir, + minion_b2_dir, + ): + dir_.mkdir(parents=True, exist_ok=True) + (dir_ / "master.d").mkdir(exist_ok=True) + # minion.d is probably needed to prevent errors on tempdir cleanup + (dir_ / "minion.d").mkdir(exist_ok=True) + (dir_ / "pki").mkdir(exist_ok=True) + (master_dir / "master.d").mkdir(exist_ok=True) - master_config_path = master_dir / "master" - master_config_path.write_text( - """ + master_config_path = master_dir / "master" + master_config_path.write_text( + """ +open_mode: True auth.pam.python: /usr/local/bin/python3 order_masters: True @@ -142,17 +122,20 @@ nodegroups: second_string: "minion_*2" b_string: "minion_b*" + """ + ) + + minion_config_path = minion_dir / "minion" + minion_config_path.write_text("id: minion\nmaster: master\nopen_mode: True") + + syndic_a_minion_config_path = syndic_a_dir / "minion" + syndic_a_minion_config_path.write_text( + "id: syndic_a\nmaster: master\nopen_mode: True" + ) + syndic_a_master_config_path = syndic_a_dir / "master" + syndic_a_master_config_path.write_text( """ - ) - - minion_config_path = minion_dir / "minion" - minion_config_path.write_text("id: minion\nmaster: master") - - syndic_a_minion_config_path = syndic_a_dir / "minion" - syndic_a_minion_config_path.write_text("id: syndic_a\nmaster: master") - syndic_a_master_config_path = syndic_a_dir / "master" - syndic_a_master_config_path.write_text( - """ +open_mode: True auth.pam.python: /usr/local/bin/python3 syndic_master: master publisher_acl: @@ -167,34 +150,36 @@ external_auth: - '*1': - test.* - file.touch - """ - ) + """ + ) - minion_a1_config_path = minion_a1_dir / "minion" - minion_a1_config_path.write_text("id: minion_a1\nmaster: syndic_a") - minion_a2_config_path = minion_a2_dir / "minion" - minion_a2_config_path.write_text("id: minion_a2\nmaster: syndic_a") + minion_a1_config_path = minion_a1_dir / "minion" + minion_a1_config_path.write_text("id: minion_a1\nmaster: syndic_a\nopen_mode: True") + minion_a2_config_path = minion_a2_dir / "minion" + minion_a2_config_path.write_text("id: minion_a2\nmaster: syndic_a\nopen_mode: True") - syndic_b_minion_config_path = syndic_b_dir / "minion" - syndic_b_minion_config_path.write_text("id: syndic_b\nmaster: master") - syndic_b_master_config_path = syndic_b_dir / "master" - syndic_b_master_config_path.write_text("syndic_master: master") + syndic_b_minion_config_path = syndic_b_dir / "minion" + syndic_b_minion_config_path.write_text( + "id: syndic_b\nmaster: master\nopen_mode: True" + ) + syndic_b_master_config_path = syndic_b_dir / "master" + syndic_b_master_config_path.write_text("syndic_master: master\nopen_mode: True") - minion_b1_config_path = minion_b1_dir / "minion" - minion_b1_config_path.write_text("id: minion_b1\nmaster: syndic_b") - minion_b2_config_path = minion_b2_dir / "minion" - minion_b2_config_path.write_text("id: minion_b2\nmaster: syndic_b") + minion_b1_config_path = minion_b1_dir / "minion" + minion_b1_config_path.write_text("id: minion_b1\nmaster: syndic_b\nopen_mode: True") + minion_b2_config_path = minion_b2_dir / "minion" + minion_b2_config_path.write_text("id: minion_b2\nmaster: syndic_b\nopen_mode: True") - yield { - "minion_dir": minion_dir, - "master_dir": master_dir, - "syndic_a_dir": syndic_a_dir, - "syndic_b_dir": syndic_b_dir, - "minion_a1_dir": minion_a1_dir, - "minion_a2_dir": minion_a2_dir, - "minion_b1_dir": minion_b1_dir, - "minion_b2_dir": minion_b2_dir, - } + return { + "minion_dir": minion_dir, + "master_dir": master_dir, + "syndic_a_dir": syndic_a_dir, + "syndic_b_dir": syndic_b_dir, + "minion_a1_dir": minion_a1_dir, + "minion_a2_dir": minion_a2_dir, + "minion_b1_dir": minion_b1_dir, + "minion_b2_dir": minion_b2_dir, + } @pytest.fixture(scope="module") @@ -202,7 +187,6 @@ def docker_master( salt_factories, syndic_network, config, - source_path, container_image_name, container_python_version, ): @@ -216,7 +200,7 @@ def docker_master( "network": syndic_network, "volumes": { config_dir: {"bind": "/etc/salt", "mode": "z"}, - source_path: { + str(CODE_DIR / "salt"): { "bind": f"/usr/local/lib/python{container_python_version}/site-packages/salt/", "mode": "z", }, @@ -241,7 +225,6 @@ def docker_minion( salt_factories, syndic_network, config, - source_path, container_image_name, container_python_version, ): @@ -255,7 +238,7 @@ def docker_minion( "network": syndic_network, "volumes": { config_dir: {"bind": "/etc/salt", "mode": "z"}, - source_path: { + str(CODE_DIR / "salt"): { "bind": f"/usr/local/lib/python{container_python_version}/site-packages/salt/", "mode": "z", }, @@ -275,7 +258,6 @@ def docker_syndic_a( salt_factories, config, syndic_network, - source_path, container_image_name, container_python_version, ): @@ -289,7 +271,7 @@ def docker_syndic_a( "network": syndic_network, "volumes": { config_dir: {"bind": "/etc/salt", "mode": "z"}, - source_path: { + str(CODE_DIR / "salt"): { "bind": f"/usr/local/lib/python{container_python_version}/site-packages/salt/", "mode": "z", }, @@ -309,7 +291,6 @@ def docker_syndic_b( salt_factories, config, syndic_network, - source_path, container_image_name, container_python_version, ): @@ -323,7 +304,7 @@ def docker_syndic_b( "network": syndic_network, "volumes": { config_dir: {"bind": "/etc/salt", "mode": "z"}, - source_path: { + str(CODE_DIR / "salt"): { "bind": f"/usr/local/lib/python{container_python_version}/site-packages/salt/", "mode": "z", }, @@ -343,7 +324,6 @@ def docker_minion_a1( salt_factories, config, syndic_network, - source_path, container_image_name, container_python_version, ): @@ -357,7 +337,7 @@ def docker_minion_a1( "entrypoint": "python -m http.server", "volumes": { config_dir: {"bind": "/etc/salt", "mode": "z"}, - source_path: { + str(CODE_DIR / "salt"): { "bind": f"/usr/local/lib/python{container_python_version}/site-packages/salt/", "mode": "z", }, @@ -377,7 +357,6 @@ def docker_minion_a2( salt_factories, config, syndic_network, - source_path, container_image_name, container_python_version, ): @@ -391,7 +370,7 @@ def docker_minion_a2( "entrypoint": "python -m http.server", "volumes": { config_dir: {"bind": "/etc/salt", "mode": "z"}, - source_path: { + str(CODE_DIR / "salt"): { "bind": f"/usr/local/lib/python{container_python_version}/site-packages/salt/", "mode": "z", }, @@ -411,7 +390,6 @@ def docker_minion_b1( salt_factories, config, syndic_network, - source_path, container_image_name, container_python_version, ): @@ -425,7 +403,7 @@ def docker_minion_b1( "entrypoint": "python -m http.server", "volumes": { config_dir: {"bind": "/etc/salt", "mode": "z"}, - source_path: { + str(CODE_DIR / "salt"): { "bind": f"/usr/local/lib/python{container_python_version}/site-packages/salt/", "mode": "z", }, @@ -445,7 +423,6 @@ def docker_minion_b2( salt_factories, config, syndic_network, - source_path, container_image_name, container_python_version, ): @@ -459,7 +436,7 @@ def docker_minion_b2( "entrypoint": "python -m http.server", "volumes": { config_dir: {"bind": "/etc/salt", "mode": "z"}, - source_path: { + str(CODE_DIR / "salt"): { "bind": f"/usr/local/lib/python{container_python_version}/site-packages/salt/", "mode": "z", }, @@ -511,23 +488,16 @@ def all_the_docker( # END WORKAROUND for s in (docker_syndic_a, docker_syndic_b): s.run("salt-syndic -d") - failure_time = time.time() + 20 - accept_keys( - container=docker_master, required_minions={"minion", "syndic_a", "syndic_b"} - ) - accept_keys( - container=docker_syndic_a, required_minions={"minion_a1", "minion_a2"} - ) - accept_keys( - container=docker_syndic_b, required_minions={"minion_b1", "minion_b2"} - ) - for tries in range(30): - res = docker_master.run(r"salt \* test.ping -t20 --out=json") + failure_time = time.time() + (5 * 60) + results = None + while time.time() < failure_time: + res = docker_master.run(r"salt \* test.ping -t10 --out=json") results = json_output_to_dict(res.stdout) if set(results).issuperset( ["minion", "minion_a1", "minion_a2", "minion_b1", "minion_b2"] ): break + time.sleep(5) else: pytest.skip(f"Missing some minions: {sorted(results)}") @@ -550,10 +520,10 @@ def all_the_docker( # res = container.run('rm -rfv /etc/salt/') # print(container) # print(res.stdout) - except docker.errors.APIError as e: + except docker.errors.APIError as exc: # if the container isn't running, there's not thing we can do # at this point. - print(f"Docker failed removing /etc/salt: {e}") + log.info(f"Docker failed removing /etc/salt: %s", exc) @pytest.fixture( @@ -782,12 +752,12 @@ def test_root_user_should_be_able_to_call_any_and_all_minions_with_any_and_all_c ): target, expected_minions = all_the_minions res = docker_master.run( - f"salt {target} {all_the_commands} -t 20 --out=json", + f"salt {target} {all_the_commands} -t 10 --out=json", ) if "jid does not exist" in (res.stderr or ""): # might be flaky, let's retry res = docker_master.run( - f"salt {target} {all_the_commands} -t 20 --out=json", + f"salt {target} {all_the_commands} -t 10 --out=json", ) results = json_output_to_dict(res.stdout) assert sorted(results) == expected_minions, res.stdout @@ -798,7 +768,7 @@ def test_eauth_user_should_be_able_to_target_valid_minions_with_valid_command( ): target, expected_minions = eauth_valid_minions res = docker_master.run( - f"salt -a pam --username bob --password '' {target} {eauth_valid_commands} {eauth_valid_arguments} -t 20 --out=json", + f"salt -a pam --username bob --password '' {target} {eauth_valid_commands} {eauth_valid_arguments} -t 10 --out=json", ) results = json_output_to_dict(res.stdout) assert sorted(results) == expected_minions, res.stdout @@ -808,7 +778,7 @@ def test_eauth_user_should_not_be_able_to_target_invalid_minions( eauth_blocked_minions, docker_master, docker_minions ): res = docker_master.run( - f"salt -a pam --username bob --password '' {eauth_blocked_minions} file.touch /tmp/bad_bad_file.txt -t 20 --out=json", + f"salt -a pam --username bob --password '' {eauth_blocked_minions} file.touch /tmp/bad_bad_file.txt -t 10 --out=json", ) assert "Authorization error occurred." == res.data or res.data is None for minion in docker_minions: @@ -823,7 +793,7 @@ def test_eauth_user_should_not_be_able_to_target_valid_minions_with_invalid_comm ): tgt, _ = eauth_valid_minions res = docker_master.run( - f"salt -a pam --username bob --password '' {tgt} {eauth_invalid_commands} -t 20 --out=json", + f"salt -a pam --username bob --password '' {tgt} {eauth_invalid_commands} -t 10 --out=json", ) results = json_output_to_dict(res.stdout) assert "Authorization error occurred" in res.stdout @@ -836,7 +806,7 @@ def test_eauth_user_should_not_be_able_to_target_valid_minions_with_valid_comman ): tgt, _ = eauth_valid_minions res = docker_master.run( - f"salt -a pam --username bob --password '' -C '{tgt}' {eauth_valid_commands} {eauth_invalid_arguments} -t 20 --out=json", + f"salt -a pam --username bob --password '' -C '{tgt}' {eauth_valid_commands} {eauth_invalid_arguments} -t 10 --out=json", ) results = json_output_to_dict(res.stdout) assert "Authorization error occurred" in res.stdout @@ -849,7 +819,7 @@ def test_invalid_eauth_user_should_not_be_able_to_do_anything( # TODO: Do we really need to run all of these tests for the invalid user? Maybe not! -W. Werner, 2022-12-01 tgt, _ = eauth_valid_minions res = docker_master.run( - f"salt -a pam --username badguy --password '' -C '{tgt}' {eauth_valid_commands} {eauth_valid_arguments} -t 20 --out=json", + f"salt -a pam --username badguy --password '' -C '{tgt}' {eauth_valid_commands} {eauth_valid_arguments} -t 10 --out=json", ) results = json_output_to_dict(res.stdout) assert sorted(results) == [] @@ -860,7 +830,7 @@ def test_root_should_be_able_to_use_comprehensive_targeting( ): tgt, expected_minions = comprehensive_minion_targeting res = docker_master.run( - f"salt -C '{tgt}' test.version -t 20 --out=json", + f"salt -C '{tgt}' test.version -t 10 --out=json", ) results = json_output_to_dict(res.stdout) assert sorted(results) == expected_minions @@ -871,7 +841,7 @@ def test_eauth_user_should_be_able_to_target_valid_minions_with_valid_commands_c ): tgt, expected_minions = valid_eauth_comprehensive_minion_targeting res = docker_master.run( - f"salt -a pam --username bob --password '' -C '{tgt}' test.version -t 20 --out=json", + f"salt -a pam --username bob --password '' -C '{tgt}' test.version -t 10 --out=json", ) results = json_output_to_dict(res.stdout) assert sorted(results) == expected_minions @@ -881,7 +851,7 @@ def test_eauth_user_with_invalid_comprehensive_targeting_should_auth_failure( invalid_comprehensive_minion_targeting, docker_master ): res = docker_master.run( - f"salt -a pam --username fnord --password '' -C '{invalid_comprehensive_minion_targeting}' test.version -t 20 --out=json", + f"salt -a pam --username fnord --password '' -C '{invalid_comprehensive_minion_targeting}' test.version -t 10 --out=json", ) results = json_output_to_dict(res.stdout) assert "Authorization error occurred" in res.stdout diff --git a/tests/pytests/integration/daemons/test_memory_leak.py b/tests/pytests/integration/daemons/test_memory_leak.py index a56a81d36be..d61bb85b736 100644 --- a/tests/pytests/integration/daemons/test_memory_leak.py +++ b/tests/pytests/integration/daemons/test_memory_leak.py @@ -6,18 +6,15 @@ import pytest pytestmark = [ pytest.mark.slow_test, + pytest.mark.timeout_unless_on_windows(360), ] @pytest.fixture -def testfile_path(tmp_path): - return tmp_path / "testfile" - - -@pytest.fixture -def file_add_delete_sls(testfile_path, base_env_state_tree_root_dir): +def file_add_delete_sls(tmp_path, salt_master): + path = tmp_path / "testfile" sls_name = "file_add" - sls_contents = """ + sls_contents = f""" add_file: file.managed: - name: {path} @@ -35,16 +32,13 @@ def file_add_delete_sls(testfile_path, base_env_state_tree_root_dir): echo: cmd.run: - name: \"echo 'This is a test!'\" - """.format( - path=testfile_path - ) - with pytest.helpers.temp_file( - f"{sls_name}.sls", sls_contents, base_env_state_tree_root_dir - ): + """ + with salt_master.state_tree.base.temp_file(f"{sls_name}.sls", sls_contents): yield sls_name @pytest.mark.skip_on_fips_enabled_platform +@pytest.mark.skip_on_windows(reason="Windows is a spawning platform, won't work") @pytest.mark.skip_on_darwin(reason="MacOS is a spawning platform, won't work") @pytest.mark.flaky(max_runs=4) def test_memory_leak(salt_cli, salt_minion, file_add_delete_sls): diff --git a/tests/pytests/integration/minion/test_job_return.py b/tests/pytests/integration/minion/test_job_return.py index 64ea3a04e81..dc345eb2771 100644 --- a/tests/pytests/integration/minion/test_job_return.py +++ b/tests/pytests/integration/minion/test_job_return.py @@ -91,9 +91,10 @@ def salt_minion_1(salt_master_1, salt_master_2): yield factory -def test_job_resturn(salt_master_1, salt_master_2, salt_minion_1): +@pytest.mark.timeout_unless_on_windows(360) +def test_job_return(salt_master_1, salt_master_2, salt_minion_1): cli = salt_master_1.salt_cli(timeout=120) - ret = cli.run("test.ping", "-v", minion_tgt="minion-1") + ret = cli.run("test.ping", "-v", minion_tgt=salt_minion_1.id) for line in ret.stdout.splitlines(): if "with jid" in line: jid = line.split("with jid")[1].strip() diff --git a/tests/pytests/integration/minion/test_return_retries.py b/tests/pytests/integration/minion/test_return_retries.py index c8e5f64674c..058cfdc32be 100644 --- a/tests/pytests/integration/minion/test_return_retries.py +++ b/tests/pytests/integration/minion/test_return_retries.py @@ -44,17 +44,19 @@ def test_publish_retry(salt_master, salt_minion_retry, salt_cli, salt_run_cli): time.sleep(5) data = None - for i in range(1, 30): + for _ in range(1, 30): time.sleep(1) data = salt_run_cli.run("jobs.lookup_jid", jid, _timeout=60).data if data: break + assert data assert salt_minion_retry.id in data assert data[salt_minion_retry.id] is True @pytest.mark.slow_test +@pytest.mark.timeout_unless_on_windows(180) def test_pillar_timeout(salt_master_factory, tmp_path): cmd = 'print(\'{"foo": "bar"}\');\n' diff --git a/tests/pytests/integration/modules/grains/test_append.py b/tests/pytests/integration/modules/grains/test_append.py index 4123d885612..f6d3fa3015b 100644 --- a/tests/pytests/integration/modules/grains/test_append.py +++ b/tests/pytests/integration/modules/grains/test_append.py @@ -106,6 +106,7 @@ def test_grains_append_val_is_list(salt_call_cli, append_grain): assert ret.data == {append_grain.key: [append_grain.value, second_grain]} +@pytest.mark.timeout_unless_on_windows(240) def test_grains_remove_add( salt_call_cli, append_grain, wait_for_pillar_refresh_complete ): diff --git a/tests/pytests/integration/modules/test_beacons.py b/tests/pytests/integration/modules/test_beacons.py index 131ebe5eb47..1a1ae274854 100644 --- a/tests/pytests/integration/modules/test_beacons.py +++ b/tests/pytests/integration/modules/test_beacons.py @@ -12,6 +12,7 @@ from tests.support.helpers import PRE_PYTEST_SKIP_OR_NOT pytestmark = [ pytest.mark.core_test, pytest.mark.windows_whitelisted, + pytest.mark.timeout_unless_on_windows(200), ] diff --git a/tests/pytests/integration/modules/test_pillar.py b/tests/pytests/integration/modules/test_pillar.py index 5db9a1630a7..eceede84493 100644 --- a/tests/pytests/integration/modules/test_pillar.py +++ b/tests/pytests/integration/modules/test_pillar.py @@ -400,6 +400,7 @@ def test_pillar_refresh_pillar_ping(salt_cli, salt_minion, key_pillar): @pytest.mark.slow_test +@pytest.mark.timeout_unless_on_windows(120) def test_pillar_refresh_pillar_scheduler(salt_master, salt_cli, salt_minion): """ Ensure schedule jobs in pillar are only updated when values change. diff --git a/tests/pytests/integration/modules/test_virt.py b/tests/pytests/integration/modules/test_virt.py index 2b29a06e8a9..8f1c71e2238 100644 --- a/tests/pytests/integration/modules/test_virt.py +++ b/tests/pytests/integration/modules/test_virt.py @@ -15,6 +15,7 @@ log = logging.getLogger(__name__) pytestmark = [ pytest.mark.slow_test, + pytest.mark.timeout_unless_on_windows(120), pytest.mark.skip_if_binaries_missing("docker"), ] diff --git a/tests/pytests/integration/modules/test_x509_v2.py b/tests/pytests/integration/modules/test_x509_v2.py index 1c64b29f493..a6a99d27f30 100644 --- a/tests/pytests/integration/modules/test_x509_v2.py +++ b/tests/pytests/integration/modules/test_x509_v2.py @@ -454,6 +454,7 @@ def x509_salt_call_cli(x509_salt_minion): return x509_salt_minion.salt_call_cli() +@pytest.mark.timeout_unless_on_windows(120) def test_sign_remote_certificate(x509_salt_call_cli, cert_args, ca_key, rsa_privkey): ret = x509_salt_call_cli.run("x509.create_certificate", **cert_args) assert ret.data diff --git a/tests/pytests/integration/netapi/test_ssh_client.py b/tests/pytests/integration/netapi/test_ssh_client.py index c2e948baf7a..9da5bb93d31 100644 --- a/tests/pytests/integration/netapi/test_ssh_client.py +++ b/tests/pytests/integration/netapi/test_ssh_client.py @@ -135,15 +135,19 @@ def test_ssh_disabled(client, auth_creds): assert ret is None +@pytest.mark.timeout_unless_on_windows(360) def test_shell_inject_ssh_priv( - client, salt_ssh_roster_file, rosters_dir, tmp_path, salt_auto_account + client, salt_ssh_roster_file, rosters_dir, tmp_path, salt_auto_account, grains ): """ Verify CVE-2020-16846 for ssh_priv variable """ + if grains["os"] == "VMware Photon OS" and grains["osmajorrelease"] == 3: + pytest.skip("Skipping problematic test on PhotonOS 3") # ZDI-CAN-11143 path = tmp_path / "test-11143" tgts = ["repo.saltproject.io", "www.zerodayinitiative.com"] + ret = None for tgt in tgts: low = { "roster": "cache", @@ -160,7 +164,9 @@ def test_shell_inject_ssh_priv( ret = client.run(low) if ret: break + assert path.exists() is False + assert ret assert not ret[tgt]["stdout"] assert ret[tgt]["stderr"] diff --git a/tests/pytests/integration/reactor/test_reactor.py b/tests/pytests/integration/reactor/test_reactor.py index 0c73d282a71..957e17876e3 100644 --- a/tests/pytests/integration/reactor/test_reactor.py +++ b/tests/pytests/integration/reactor/test_reactor.py @@ -104,6 +104,7 @@ def test_reactor_reaction( @pytest.mark.skip_on_windows(reason=PRE_PYTEST_SKIP_REASON) +@pytest.mark.timeout_unless_on_windows(120) def test_reactor_is_leader( event_listener, salt_master, diff --git a/tests/pytests/integration/runners/state/orchestrate/test_events.py b/tests/pytests/integration/runners/state/orchestrate/test_events.py index b1cde89ba73..4f0ecccb406 100644 --- a/tests/pytests/integration/runners/state/orchestrate/test_events.py +++ b/tests/pytests/integration/runners/state/orchestrate/test_events.py @@ -419,6 +419,7 @@ def test_orchestration_onchanges_and_prereq( @pytest.mark.skip_if_not_root @pytest.mark.skip_on_windows @pytest.mark.skip_on_darwin +@pytest.mark.timeout_unless_on_windows(120) def test_unknown_in_runner_event( runner_salt_run_cli, runner_salt_master, diff --git a/tests/pytests/integration/runners/state/orchestrate/test_orchestrate.py b/tests/pytests/integration/runners/state/orchestrate/test_orchestrate.py index e4a5c184f3c..013a01e498c 100644 --- a/tests/pytests/integration/runners/state/orchestrate/test_orchestrate.py +++ b/tests/pytests/integration/runners/state/orchestrate/test_orchestrate.py @@ -489,6 +489,7 @@ def _check_skip(grains): return False +@pytest.mark.timeout_unless_on_windows(120) @pytest.mark.skip_initial_gh_actions_failure(skip=_check_skip) def test_orchestrate_subset( salt_run_cli, diff --git a/tests/pytests/integration/runners/test_vault.py b/tests/pytests/integration/runners/test_vault.py index 818745d23a4..8dc1f4689e6 100644 --- a/tests/pytests/integration/runners/test_vault.py +++ b/tests/pytests/integration/runners/test_vault.py @@ -27,6 +27,7 @@ pytestmark = [ pytest.mark.slow_test, pytest.mark.skip_if_binaries_missing("dockerd", "vault", "getent"), pytest.mark.usefixtures("vault_container_version"), + pytest.mark.timeout_unless_on_windows(120), ] diff --git a/tests/pytests/integration/states/test_ansiblegate.py b/tests/pytests/integration/states/test_ansiblegate.py index c66ba9075b4..ef08768b1c7 100644 --- a/tests/pytests/integration/states/test_ansiblegate.py +++ b/tests/pytests/integration/states/test_ansiblegate.py @@ -65,6 +65,7 @@ def ansible_inventory(ansible_inventory_directory, sshd_server): @pytest.mark.requires_sshd_server +@pytest.mark.timeout_unless_on_windows(240) def test_ansible_playbook(salt_call_cli, ansible_inventory, tmp_path): rundir = tmp_path / "rundir" rundir.mkdir(exist_ok=True, parents=True) @@ -117,7 +118,7 @@ def test_ansible_playbook(salt_call_cli, ansible_inventory, tmp_path): except FactoryTimeout: log.debug("%s took longer than %s seconds", name, timeout) if timeout == timeouts[-1]: - pytest.fail("Failed to run {}".format(name)) + pytest.fail(f"Failed to run {name}") else: assert ret.returncode == 0 assert StateResult(ret.data).result is True diff --git a/tests/pytests/integration/states/test_beacon.py b/tests/pytests/integration/states/test_beacon.py index 586664cb560..5d6737e6a18 100644 --- a/tests/pytests/integration/states/test_beacon.py +++ b/tests/pytests/integration/states/test_beacon.py @@ -9,6 +9,7 @@ log = logging.getLogger(__name__) @pytest.mark.slow_test +@pytest.mark.timeout_unless_on_windows(240) def test_present_absent(salt_master, salt_minion, salt_call_cli): ret = salt_call_cli.run("beacons.reset") diff --git a/tests/pytests/integration/states/test_pip_state.py b/tests/pytests/integration/states/test_pip_state.py new file mode 100644 index 00000000000..ed3dca22298 --- /dev/null +++ b/tests/pytests/integration/states/test_pip_state.py @@ -0,0 +1,75 @@ +import subprocess + +import pytest + +import salt.version +from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES +from tests.support.helpers import VirtualEnv, dedent + +pytestmark = [ + pytest.mark.skip_if_binaries_missing(*KNOWN_BINARY_NAMES, check_all=False), + pytest.mark.requires_network, +] + + +@pytest.fixture(scope="module") +def _extra_requirements(): + extra_requirements = [] + for name, version in salt.version.dependency_information(): + if name in ["PyYAML", "packaging", "looseversion"]: + extra_requirements.append(f"{name}=={version}") + return extra_requirements + + +@pytest.mark.slow_test +@pytest.mark.parametrize( + "pip_contraint", + [ + # Latest pip 18 + "<19.0", + # Latest pip 19 + "<20.0", + # Latest pip 20 + "<21.0", + # Latest pip + None, + ], +) +def test_importable_installation_error(_extra_requirements, pip_contraint): + code = dedent( + """\ + import sys + import traceback + try: + import salt.states.pip_state + salt.states.pip_state.InstallationError + except ImportError as exc: + traceback.print_exc(file=sys.stdout) + sys.stdout.flush() + sys.exit(1) + except AttributeError as exc: + traceback.print_exc(file=sys.stdout) + sys.stdout.flush() + sys.exit(2) + except Exception as exc: + traceback.print_exc(exc, file=sys.stdout) + sys.stdout.flush() + sys.exit(3) + sys.exit(0) + """ + ) + with VirtualEnv() as venv: + venv.install(*_extra_requirements) + if pip_contraint: + venv.install(f"pip{pip_contraint}") + try: + subprocess.check_output([venv.venv_python, "-c", code]) + except subprocess.CalledProcessError as exc: + if exc.returncode == 1: + pytest.fail(f"Failed to import pip:\n{exc.output}") + elif exc.returncode == 2: + pytest.fail( + f"Failed to import InstallationError from pip:\n{exc.output}" + ) + else: + pytest.fail(exc.output) diff --git a/tests/pytests/integration/states/test_x509_v2.py b/tests/pytests/integration/states/test_x509_v2.py index 84196f746eb..f127c183b35 100644 --- a/tests/pytests/integration/states/test_x509_v2.py +++ b/tests/pytests/integration/states/test_x509_v2.py @@ -33,6 +33,7 @@ log = logging.getLogger(__name__) pytestmark = [ pytest.mark.slow_test, + pytest.mark.timeout_unless_on_windows(120), pytest.mark.skipif(HAS_LIBS is False, reason="Needs cryptography library"), ] diff --git a/tests/pytests/pkg/downgrade/test_salt_downgrade.py b/tests/pytests/pkg/downgrade/test_salt_downgrade.py index f6a8ef17a23..ec090d17dda 100644 --- a/tests/pytests/pkg/downgrade/test_salt_downgrade.py +++ b/tests/pytests/pkg/downgrade/test_salt_downgrade.py @@ -1,4 +1,5 @@ import packaging.version +import psutil import pytest from pytestskipmarkers.utils import platform @@ -34,6 +35,19 @@ def test_salt_downgrade(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 platform.is_windows(): + process_name = "salt-minion.exe" + else: + process_name = "salt-minion" + old_pid = None + for proc in psutil.process_iter(): + if process_name in proc.name(): + if psutil.Process(proc.ppid()).name() != process_name: + old_pid = proc.pid + break + assert old_pid is not None + # Downgrade Salt to the previous version and test install_salt.install(downgrade=True) bin_file = "salt" @@ -45,6 +59,17 @@ def test_salt_downgrade(salt_call_cli, install_salt): 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 = None + for proc in psutil.process_iter(): + if process_name in proc.name(): + if psutil.Process(proc.ppid()).name() != process_name: + new_pid = proc.pid + break + assert new_pid is not None + assert new_pid != old_pid + ret = install_salt.proc.run(bin_file, "--version") assert ret.returncode == 0 assert packaging.version.parse( diff --git a/tests/pytests/pkg/integration/test_enabled_disabled.py b/tests/pytests/pkg/integration/test_enabled_disabled.py index c6f0d75db8f..b2293492701 100644 --- a/tests/pytests/pkg/integration/test_enabled_disabled.py +++ b/tests/pytests/pkg/integration/test_enabled_disabled.py @@ -7,24 +7,14 @@ def test_services(install_salt, salt_cli, salt_minion): """ Check if Services are enabled/disabled """ + services_disabled = [] + services_enabled = [] if install_salt.distro_id in ("ubuntu", "debian"): services_enabled = ["salt-master", "salt-minion", "salt-syndic", "salt-api"] - services_disabled = [] elif install_salt.distro_id in ("centos", "redhat", "amzn", "fedora"): - services_enabled = [] services_disabled = ["salt-master", "salt-minion", "salt-syndic", "salt-api"] elif install_salt.distro_id == "photon": - if float(install_salt.distro_version) < 5: - services_enabled = [] - services_disabled = [ - "salt-master", - "salt-minion", - "salt-syndic", - "salt-api", - ] - else: - services_enabled = ["salt-master", "salt-minion", "salt-syndic", "salt-api"] - services_disabled = [] + services_enabled = ["salt-master", "salt-minion", "salt-syndic", "salt-api"] elif platform.is_darwin(): services_enabled = ["salt-minion"] services_disabled = [] diff --git a/tests/pytests/pkg/upgrade/test_salt_upgrade.py b/tests/pytests/pkg/upgrade/test_salt_upgrade.py index d844b5807da..241a3c63d0f 100644 --- a/tests/pytests/pkg/upgrade/test_salt_upgrade.py +++ b/tests/pytests/pkg/upgrade/test_salt_upgrade.py @@ -1,5 +1,7 @@ import packaging.version +import psutil import pytest +from pytestskipmarkers.utils import platform def test_salt_upgrade(salt_call_cli, install_salt): @@ -29,6 +31,19 @@ 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 platform.is_windows(): + process_name = "salt-minion.exe" + else: + process_name = "salt-minion" + old_pid = None + for proc in psutil.process_iter(): + if process_name in proc.name(): + if psutil.Process(proc.ppid()).name() != process_name: + old_pid = proc.pid + break + assert old_pid is not None + # Upgrade Salt from previous version and test install_salt.install(upgrade=True) ret = salt_call_cli.run("test.version") @@ -37,8 +52,20 @@ def test_salt_upgrade(salt_call_cli, install_salt): 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 + new_pid = None + for proc in psutil.process_iter(): + if process_name in proc.name(): + if psutil.Process(proc.ppid()).name() != process_name: + new_pid = proc.pid + break + assert new_pid is not None + assert new_pid != old_pid + 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 diff --git a/tests/pytests/unit/_logging/handlers/test_deferred_stream_handler.py b/tests/pytests/unit/_logging/handlers/test_deferred_stream_handler.py index 76b0e88eca5..62c0dff4be4 100644 --- a/tests/pytests/unit/_logging/handlers/test_deferred_stream_handler.py +++ b/tests/pytests/unit/_logging/handlers/test_deferred_stream_handler.py @@ -9,6 +9,7 @@ import pytest from pytestshellutils.utils.processes import terminate_process from salt._logging.handlers import DeferredStreamHandler +from salt._logging.impl import set_lowest_log_level from salt.utils.nb_popen import NonBlockingPopen from tests.support.helpers import CaptureOutput, dedent from tests.support.runtests import RUNTIME_VARS @@ -20,7 +21,7 @@ def _sync_with_handlers_proc_target(): with CaptureOutput() as stds: handler = DeferredStreamHandler(sys.stderr) - handler.setLevel(logging.DEBUG) + set_lowest_log_level(logging.DEBUG) formatter = logging.Formatter("%(message)s") handler.setFormatter(formatter) logging.root.addHandler(handler) @@ -45,7 +46,7 @@ def _deferred_write_on_flush_proc_target(): with CaptureOutput() as stds: handler = DeferredStreamHandler(sys.stderr) - handler.setLevel(logging.DEBUG) + set_lowest_log_level(logging.DEBUG) formatter = logging.Formatter("%(message)s") handler.setFormatter(formatter) logging.root.addHandler(handler) @@ -126,7 +127,7 @@ def test_deferred_write_on_atexit(tmp_path): # Just loop consuming output while True: if time.time() > max_time: - pytest.fail("Script didn't exit after {} second".format(execution_time)) + pytest.fail(f"Script didn't exit after {execution_time} second") time.sleep(0.125) _out = proc.recv() @@ -146,7 +147,7 @@ def test_deferred_write_on_atexit(tmp_path): finally: terminate_process(proc.pid, kill_children=True) if b"Foo" not in err: - pytest.fail("'Foo' should be in stderr and it's not: {}".format(err)) + pytest.fail(f"'Foo' should be in stderr and it's not: {err}") @pytest.mark.skip_on_windows(reason="Windows does not support SIGINT") diff --git a/tests/pytests/unit/cloud/test_cloud.py b/tests/pytests/unit/cloud/test_cloud.py index b227a7977f0..8785e086ecd 100644 --- a/tests/pytests/unit/cloud/test_cloud.py +++ b/tests/pytests/unit/cloud/test_cloud.py @@ -5,6 +5,10 @@ from salt.config import get_cloud_config_value from salt.exceptions import SaltCloudSystemExit from tests.support.mock import MagicMock, patch +pytestmark = [ + pytest.mark.timeout_unless_on_windows(120), +] + @pytest.fixture def master_config(master_opts): diff --git a/tests/pytests/unit/fileserver/test_s3fs.py b/tests/pytests/unit/fileserver/test_s3fs.py index b50424a1d1a..2c14738a68c 100644 --- a/tests/pytests/unit/fileserver/test_s3fs.py +++ b/tests/pytests/unit/fileserver/test_s3fs.py @@ -1,19 +1,130 @@ +import os + +import boto3 import pytest +import yaml + +# moto must be imported before boto3 +from moto import mock_s3 import salt.fileserver.s3fs as s3fs +import salt.utils.s3 @pytest.fixture -def configure_loader_modules(tmp_path): +def bucket(): + with mock_s3(): + yield "mybucket" + + +@pytest.fixture(scope="module") +def aws_creds(): + return { + "aws_access_key_id": "testing", + "aws_secret_access_key": "testing", + "aws_session_token": "testing", + "region_name": "us-east-1", + } + + +@pytest.fixture(scope="function") +def configure_loader_modules(tmp_path, bucket): opts = { "cachedir": tmp_path, + "s3.buckets": {"base": [bucket]}, + "s3.location": "us-east-1", + "s3.s3_cache_expire": -1, } - return {s3fs: {"__opts__": opts}} + utils = {"s3.query": salt.utils.s3.query} + + yield {s3fs: {"__opts__": opts, "__utils__": utils}} -def test_cache_round_trip(): +@pytest.fixture(scope="function") +def s3(bucket, aws_creds): + conn = boto3.client("s3", **aws_creds) + conn.create_bucket(Bucket=bucket) + return conn + + +def make_keys(bucket, conn, keys): + for key, data in keys.items(): + conn.put_object( + Bucket=bucket, + Key=key, + Body=data["content"], + ) + + +def verify_cache(bucket, expected): + for key, data in expected.items(): + correct_content = data["content"] + cache_file = s3fs._get_cached_file_name(bucket, "base", key) + assert os.path.exists(cache_file) + + if correct_content is None: + continue + + with salt.utils.files.fopen(cache_file) as f: + content = f.read() + assert correct_content == content + + +@pytest.mark.skip_on_fips_enabled_platform +def test_update(bucket, s3): + """Tests that files get downloaded from s3 to the local cache.""" + + keys = { + "top.sls": {"content": yaml.dump({"base": {"*": ["foo"]}})}, + "foo.sls": {"content": yaml.dump({"nginx": {"pkg.installed": []}})}, + "files/nginx.conf": {"content": "server {}"}, + } + + make_keys(bucket, s3, keys) + s3fs.update() + verify_cache(bucket, keys) + + # make a modification and update again - verify the change is retrieved + keys["top.sls"]["content"] = yaml.dump({"base": {"*": ["foo", "bar"]}}) + make_keys(bucket, s3, keys) + s3fs.update() + verify_cache(bucket, keys) + + +@pytest.mark.skip_on_fips_enabled_platform +def test_s3_hash(bucket, s3): + """Verifies that s3fs hashes files correctly.""" + + keys = { + "top.sls": {"content": yaml.dump({"base": {"*": ["foo"]}})}, + "foo.sls": {"content": yaml.dump({"nginx": {"pkg.installed": []}})}, + "files/nginx.conf": {"content": "server {}"}, + } + + make_keys(bucket, s3, keys) + s3fs.update() + + for key, item in keys.items(): + cached_file_path = s3fs._get_cached_file_name(bucket, "base", key) + item["hash"] = salt.utils.hashutils.get_hash( + cached_file_path, s3fs.S3_HASH_TYPE + ) + item["cached_file_path"] = cached_file_path + + load = {"saltenv": "base"} + fnd = {"bucket": bucket} + + for key, item in keys.items(): + fnd["path"] = item["cached_file_path"] + actual_hash = s3fs.file_hash(load, fnd) + assert s3fs.S3_HASH_TYPE == actual_hash["hash_type"] + assert item["hash"] == actual_hash["hsum"] + + +@pytest.mark.skip_on_fips_enabled_platform +def test_cache_round_trip(bucket): metadata = {"foo": "bar"} - cache_file = s3fs._get_cached_file_name("base", "fake_bucket", "some_file") + cache_file = s3fs._get_cached_file_name(bucket, "base", "somefile") s3fs._write_buckets_cache_file(metadata, cache_file) assert s3fs._read_buckets_cache_file(cache_file) == metadata diff --git a/tests/pytests/unit/grains/test_core.py b/tests/pytests/unit/grains/test_core.py index e58a1e99960..8849b542bf0 100644 --- a/tests/pytests/unit/grains/test_core.py +++ b/tests/pytests/unit/grains/test_core.py @@ -2354,6 +2354,7 @@ def test_fqdns_return(): @pytest.mark.skip_unless_on_linux +@pytest.mark.timeout(60) def test_fqdns_socket_error(caplog): """ test the behavior on non-critical socket errors of the dns grain @@ -2895,6 +2896,10 @@ def test_virtual_has_virtual_grain(): assert virtual_grains["virtual"] != "physical" +def test__windows_platform_data(): + pass + + @pytest.mark.skip_unless_on_windows @pytest.mark.parametrize( ("osdata", "expected"), @@ -2902,6 +2907,13 @@ def test_virtual_has_virtual_grain(): ({"kernel": "Not Windows"}, {}), ({"kernel": "Windows"}, {"virtual": "physical"}), ({"kernel": "Windows", "manufacturer": "QEMU"}, {"virtual": "kvm"}), + ({"kernel": "Windows", "biosstring": "VRTUAL"}, {"virtual": "HyperV"}), + ({"kernel": "Windows", "biosstring": "A M I"}, {"virtual": "VirtualPC"}), + ( + {"kernel": "Windows", "biosstring": "Xen", "productname": "HVM domU"}, + {"virtual": "Xen", "virtual_subtype": "HVM domU"}, + ), + ({"kernel": "Windows", "biosstring": "AMAZON"}, {"virtual": "EC2"}), ({"kernel": "Windows", "manufacturer": "Bochs"}, {"virtual": "kvm"}), ( {"kernel": "Windows", "productname": "oVirt"}, @@ -2911,10 +2923,6 @@ def test_virtual_has_virtual_grain(): {"kernel": "Windows", "productname": "RHEV Hypervisor"}, {"virtual": "kvm", "virtual_subtype": "rhev"}, ), - ( - {"kernel": "Windows", "productname": "CloudStack KVM Hypervisor"}, - {"virtual": "kvm", "virtual_subtype": "cloudstack"}, - ), ( {"kernel": "Windows", "productname": "VirtualBox"}, {"virtual": "VirtualBox"}, @@ -2942,6 +2950,7 @@ def test_virtual_has_virtual_grain(): }, {"virtual": "VirtualPC"}, ), + ({"kernel": "Windows", "productname": "OpenStack"}, {"virtual": "OpenStack"}), ( {"kernel": "Windows", "manufacturer": "Parallels Software"}, {"virtual": "Parallels"}, @@ -2950,6 +2959,10 @@ def test_virtual_has_virtual_grain(): {"kernel": "Windows", "manufacturer": None, "productname": None}, {"virtual": "physical"}, ), + ( + {"kernel": "Windows", "productname": "CloudStack KVM Hypervisor"}, + {"virtual": "kvm", "virtual_subtype": "cloudstack"}, + ), ], ) def test__windows_virtual(osdata, expected): @@ -2970,17 +2983,7 @@ def test_windows_virtual_set_virtual_grain(): _, ) = platform.uname() - with patch.dict( - core.__salt__, - { - "cmd.run": salt.modules.cmdmod.run, - "cmd.run_all": salt.modules.cmdmod.run_all, - "cmd.retcode": salt.modules.cmdmod.retcode, - "smbios.get": salt.modules.smbios.get, - }, - ): - - virtual_grains = core._windows_virtual(osdata) + virtual_grains = core._windows_virtual(osdata) assert "virtual" in virtual_grains @@ -2998,46 +3001,15 @@ def test_windows_virtual_has_virtual_grain(): _, ) = platform.uname() - with patch.dict( - core.__salt__, - { - "cmd.run": salt.modules.cmdmod.run, - "cmd.run_all": salt.modules.cmdmod.run_all, - "cmd.retcode": salt.modules.cmdmod.retcode, - "smbios.get": salt.modules.smbios.get, - }, - ): - - virtual_grains = core._windows_virtual(osdata) + virtual_grains = core._windows_virtual(osdata) assert "virtual" in virtual_grains - assert virtual_grains["virtual"] != "physical" @pytest.mark.skip_unless_on_windows def test_osdata_virtual_key_win(): - with patch.dict( - core.__salt__, - { - "cmd.run": salt.modules.cmdmod.run, - "cmd.run_all": salt.modules.cmdmod.run_all, - "cmd.retcode": salt.modules.cmdmod.retcode, - "smbios.get": salt.modules.smbios.get, - }, - ): - - _windows_platform_data_ret = core.os_data() - _windows_platform_data_ret["virtual"] = "something" - - with patch.object( - core, "_windows_platform_data", return_value=_windows_platform_data_ret - ) as _windows_platform_data: - - osdata_grains = core.os_data() - _windows_platform_data.assert_called_once() - - assert "virtual" in osdata_grains - assert osdata_grains["virtual"] != "physical" + osdata_grains = core.os_data() + assert "virtual" in osdata_grains @pytest.mark.skip_unless_on_linux diff --git a/tests/pytests/unit/grains/test_core_windows_platform_data.py b/tests/pytests/unit/grains/test_core_windows_platform_data.py new file mode 100644 index 00000000000..6c5865bb034 --- /dev/null +++ b/tests/pytests/unit/grains/test_core_windows_platform_data.py @@ -0,0 +1,258 @@ +import pytest + +import salt.grains.core as core +from tests.support.mock import MagicMock, Mock, patch + +pytestmark = [ + pytest.mark.skip_unless_on_windows, +] + +wmi = pytest.importorskip("wmi", reason="WMI only available on Windows") + + +def test__windows_platform_data_index_errors(): + # mock = [MagicMock(Manufacturer="Dell Inc.", Model="Precision 5820 Tower")] + # mock = [MagicMock( + # Version="10.0.22631", + # Caption="Microsoft Windows 11 Enterprise", + # Manufacturer="Microsoft Corporation", + # ProductType=1, + # )] + + WMI = Mock() + platform_version = "1.2.3" + os_version_info = {"Domain": "test", "DomainType": "test_type"} + + with patch("salt.utils.winapi.Com", MagicMock()), patch.object( + wmi, "WMI", Mock(return_value=WMI) + ), patch.object(WMI, "Win32_ComputerSystem", return_value=[]), patch.object( + WMI, "Win32_OperatingSystem", return_value=[] + ), patch.object( + WMI, "Win32_BIOS", return_value=[] + ), patch.object( + WMI, "Win32_TimeZone", return_value=[] + ), patch.object( + WMI, "Win32_ComputerSystemProduct", return_value=[] + ), patch.object( + WMI, "Win32_BaseBoard", return_value=[] + ), patch( + "platform.version", return_value=platform_version + ), patch( + "salt.utils.win_osinfo.get_join_info", return_value=os_version_info + ): + result = core._windows_platform_data() + expected = { + "biosstring": None, + "biosversion": None, + "kernelrelease": None, + "kernelversion": platform_version, + "manufacturer": None, + "motherboard": {"productname": None, "serialnumber": None}, + "osfullname": None, + "osmanufacturer": None, + "osrelease": None, + "osservicepack": None, + "osversion": None, + "productname": None, + "serialnumber": None, + "timezone": None, + "uuid": None, + "windowsdomain": os_version_info["Domain"], + "windowsdomaintype": os_version_info["DomainType"], + } + assert result == expected + + +def test__windows_platform_data_computer_system(): + mock = [MagicMock(Manufacturer="Dell Inc.", Model="Precision 5820 Tower")] + WMI = Mock() + platform_version = "1.2.3" + os_version_info = {"Domain": "test", "DomainType": "test_type"} + with patch("salt.utils.winapi.Com", MagicMock()), patch.object( + wmi, "WMI", Mock(return_value=WMI) + ), patch.object(WMI, "Win32_ComputerSystem", return_value=mock), patch.object( + WMI, "Win32_OperatingSystem", return_value=[] + ), patch.object( + WMI, "Win32_BIOS", return_value=[] + ), patch.object( + WMI, "Win32_TimeZone", return_value=[] + ), patch.object( + WMI, "Win32_ComputerSystemProduct", return_value=[] + ), patch.object( + WMI, "Win32_BaseBoard", return_value=[] + ), patch( + "platform.version", return_value=platform_version + ), patch( + "salt.utils.win_osinfo.get_join_info", return_value=os_version_info + ): + result = core._windows_platform_data() + assert result["manufacturer"] == "Dell Inc." + assert result["productname"] == "Precision 5820 Tower" + + +def test__windows_platform_data_operating_system(): + mock = [ + MagicMock( + Version="10.0.22631", + Caption="Microsoft Windows 11 Enterprise", + Manufacturer="Microsoft Corporation", + ProductType=1, + ) + ] + + WMI = Mock() + platform_version = "1.2.3" + os_version_info = {"Domain": "test", "DomainType": "test_type"} + with patch("salt.utils.winapi.Com", MagicMock()), patch.object( + wmi, "WMI", Mock(return_value=WMI) + ), patch.object(WMI, "Win32_ComputerSystem", return_value=[]), patch.object( + WMI, "Win32_OperatingSystem", return_value=mock + ), patch.object( + WMI, "Win32_BIOS", return_value=[] + ), patch.object( + WMI, "Win32_TimeZone", return_value=[] + ), patch.object( + WMI, "Win32_ComputerSystemProduct", return_value=[] + ), patch.object( + WMI, "Win32_BaseBoard", return_value=[] + ), patch( + "platform.version", return_value=platform_version + ), patch( + "salt.utils.win_osinfo.get_join_info", return_value=os_version_info + ): + result = core._windows_platform_data() + assert result["kernelrelease"] == "10.0.22631" + assert result["osfullname"] == "Microsoft Windows 11 Enterprise" + assert result["osmanufacturer"] == "Microsoft Corporation" + assert result["osrelease"] == "11" + assert result["osversion"] == "10.0.22631" + + +def test__windows_platform_data_bios(): + mock = [ + MagicMock( + Name="11.22.33", + Version="DELL - 1072009", + SerialNumber="BCF3H13", + ) + ] + + WMI = Mock() + platform_version = "1.2.3" + os_version_info = {"Domain": "test", "DomainType": "test_type"} + with patch("salt.utils.winapi.Com", MagicMock()), patch.object( + wmi, "WMI", Mock(return_value=WMI) + ), patch.object(WMI, "Win32_ComputerSystem", return_value=[]), patch.object( + WMI, "Win32_OperatingSystem", return_value=[] + ), patch.object( + WMI, "Win32_BIOS", return_value=mock + ), patch.object( + WMI, "Win32_TimeZone", return_value=[] + ), patch.object( + WMI, "Win32_ComputerSystemProduct", return_value=[] + ), patch.object( + WMI, "Win32_BaseBoard", return_value=[] + ), patch( + "platform.version", return_value=platform_version + ), patch( + "salt.utils.win_osinfo.get_join_info", return_value=os_version_info + ): + result = core._windows_platform_data() + assert result["biosversion"] == "11.22.33" + assert result["biosstring"] == "DELL - 1072009" + assert result["serialnumber"] == "BCF3H13" + + +def test__windows_platform_data_timezone(): + mock = [ + MagicMock( + Description="(UTC-07:00) Mountain Time (US & Canada)", + ) + ] + + WMI = Mock() + platform_version = "1.2.3" + os_version_info = {"Domain": "test", "DomainType": "test_type"} + with patch("salt.utils.winapi.Com", MagicMock()), patch.object( + wmi, "WMI", Mock(return_value=WMI) + ), patch.object(WMI, "Win32_ComputerSystem", return_value=[]), patch.object( + WMI, "Win32_OperatingSystem", return_value=[] + ), patch.object( + WMI, "Win32_BIOS", return_value=[] + ), patch.object( + WMI, "Win32_TimeZone", return_value=mock + ), patch.object( + WMI, "Win32_ComputerSystemProduct", return_value=[] + ), patch.object( + WMI, "Win32_BaseBoard", return_value=[] + ), patch( + "platform.version", return_value=platform_version + ), patch( + "salt.utils.win_osinfo.get_join_info", return_value=os_version_info + ): + result = core._windows_platform_data() + assert result["timezone"] == "(UTC-07:00) Mountain Time (US & Canada)" + + +def test__windows_platform_data_computer_system_product(): + mock = [ + MagicMock( + UUID="4C4C4544-0043-4610-8030-C2C04F483033", + ) + ] + + WMI = Mock() + platform_version = "1.2.3" + os_version_info = {"Domain": "test", "DomainType": "test_type"} + with patch("salt.utils.winapi.Com", MagicMock()), patch.object( + wmi, "WMI", Mock(return_value=WMI) + ), patch.object(WMI, "Win32_ComputerSystem", return_value=[]), patch.object( + WMI, "Win32_OperatingSystem", return_value=[] + ), patch.object( + WMI, "Win32_BIOS", return_value=[] + ), patch.object( + WMI, "Win32_TimeZone", return_value=[] + ), patch.object( + WMI, "Win32_ComputerSystemProduct", return_value=mock + ), patch.object( + WMI, "Win32_BaseBoard", return_value=[] + ), patch( + "platform.version", return_value=platform_version + ), patch( + "salt.utils.win_osinfo.get_join_info", return_value=os_version_info + ): + result = core._windows_platform_data() + assert result["uuid"] == "4c4c4544-0043-4610-8030-c2c04f483033" + + +def test__windows_platform_data_baseboard(): + mock = [ + MagicMock( + Product="002KVM", + SerialNumber="/BCF0H03/CNFCW0097F00TM/", + ) + ] + + WMI = Mock() + platform_version = "1.2.3" + os_version_info = {"Domain": "test", "DomainType": "test_type"} + with patch("salt.utils.winapi.Com", MagicMock()), patch.object( + wmi, "WMI", Mock(return_value=WMI) + ), patch.object(WMI, "Win32_ComputerSystem", return_value=[]), patch.object( + WMI, "Win32_OperatingSystem", return_value=[] + ), patch.object( + WMI, "Win32_BIOS", return_value=[] + ), patch.object( + WMI, "Win32_TimeZone", return_value=[] + ), patch.object( + WMI, "Win32_ComputerSystemProduct", return_value=[] + ), patch.object( + WMI, "Win32_BaseBoard", return_value=mock + ), patch( + "platform.version", return_value=platform_version + ), patch( + "salt.utils.win_osinfo.get_join_info", return_value=os_version_info + ): + result = core._windows_platform_data() + assert result["motherboard"]["productname"] == "002KVM" + assert result["motherboard"]["serialnumber"] == "/BCF0H03/CNFCW0097F00TM/" diff --git a/tests/pytests/unit/modules/test_cassandra_mod.py b/tests/pytests/unit/modules/test_cassandra_mod.py deleted file mode 100644 index a2c3f95d852..00000000000 --- a/tests/pytests/unit/modules/test_cassandra_mod.py +++ /dev/null @@ -1,129 +0,0 @@ -""" - :codeauthor: Rupesh Tare - - Test cases for salt.modules.cassandra_mod -""" - -import pytest - -import salt.modules.cassandra_mod as cassandra -from tests.support.mock import MagicMock, patch - - -@pytest.fixture -def configure_loader_modules(): - return {cassandra: {}} - - -def test_compactionstats(): - """ - Test for Return compactionstats info - """ - mock = MagicMock(return_value="A") - with patch.object(cassandra, "_nodetool", mock): - assert cassandra.compactionstats() == "A" - - -def test_version(): - """ - Test for Return the cassandra version - """ - mock = MagicMock(return_value="A") - with patch.object(cassandra, "_nodetool", mock): - assert cassandra.version() == "A" - - -def test_netstats(): - """ - Test for Return netstats info - """ - mock = MagicMock(return_value="A") - with patch.object(cassandra, "_nodetool", mock): - assert cassandra.netstats() == "A" - - -def test_tpstats(): - """ - Test for Return tpstats info - """ - mock = MagicMock(return_value="A") - with patch.object(cassandra, "_nodetool", mock): - assert cassandra.tpstats() == "A" - - -def test_info(): - """ - Test for Return cassandra node info - """ - mock = MagicMock(return_value="A") - with patch.object(cassandra, "_nodetool", mock): - assert cassandra.info() == "A" - - -def test_ring(): - """ - Test for Return ring info - """ - mock = MagicMock(return_value="A") - with patch.object(cassandra, "_nodetool", mock): - assert cassandra.ring() == "A" - - -def test_keyspaces(): - """ - Test for Return existing keyspaces - """ - mock_keyspaces = ["A", "B", "C", "D"] - - class MockSystemManager: - def list_keyspaces(self): - return mock_keyspaces - - mock_sys_mgr = MagicMock(return_value=MockSystemManager()) - - with patch.object(cassandra, "_sys_mgr", mock_sys_mgr): - assert cassandra.keyspaces() == mock_keyspaces - - -def test_column_families(): - """ - Test for Return existing column families for all keyspaces - """ - mock_keyspaces = ["A", "B"] - - class MockSystemManager: - def list_keyspaces(self): - return mock_keyspaces - - def get_keyspace_column_families(self, keyspace): - if keyspace == "A": - return {"a": "saltines", "b": "biscuits"} - if keyspace == "B": - return {"c": "cheese", "d": "crackers"} - - mock_sys_mgr = MagicMock(return_value=MockSystemManager()) - - with patch.object(cassandra, "_sys_mgr", mock_sys_mgr): - assert cassandra.column_families("Z") is None - assert cassandra.column_families("A") == ["a", "b"] - assert cassandra.column_families() == {"A": ["a", "b"], "B": ["c", "d"]} - - -def test_column_family_definition(): - """ - Test for Return a dictionary of column family definitions for the given - keyspace/column_family - """ - - class MockSystemManager: - def get_keyspace_column_families(self, keyspace): - if keyspace == "A": - return {"a": object, "b": object} - if keyspace == "B": - raise Exception - - mock_sys_mgr = MagicMock(return_value=MockSystemManager()) - - with patch.object(cassandra, "_sys_mgr", mock_sys_mgr): - assert cassandra.column_family_definition("A", "a") == vars(object) - assert cassandra.column_family_definition("B", "a") is None diff --git a/tests/pytests/unit/modules/test_hashutil.py b/tests/pytests/unit/modules/test_hashutil.py index c91e99ce6b7..57a237bad66 100644 --- a/tests/pytests/unit/modules/test_hashutil.py +++ b/tests/pytests/unit/modules/test_hashutil.py @@ -6,6 +6,7 @@ import pytest import salt.modules.hashutil as hashutil +from tests.support.mock import patch @pytest.fixture @@ -84,3 +85,9 @@ def test_hmac_compute(the_string, the_string_hmac_compute): def test_github_signature(the_string, the_string_github): assert hashutil.github_signature(the_string, "shared secret", the_string_github) + + +def test_github_signature_uses_hmac_compare_digest(the_string, the_string_github): + with patch("hmac.compare_digest") as hmac_compare: + assert hashutil.github_signature(the_string, "shared secret", the_string_github) + hmac_compare.assert_called_once() diff --git a/tests/pytests/unit/modules/test_mac_shadow.py b/tests/pytests/unit/modules/test_mac_shadow.py new file mode 100644 index 00000000000..fa294b6651d --- /dev/null +++ b/tests/pytests/unit/modules/test_mac_shadow.py @@ -0,0 +1,124 @@ +""" +Unit Tests for the mac_desktop execution module. +""" +from datetime import datetime + +import pytest + +import salt.modules.mac_shadow as mac_shadow +from salt.exceptions import CommandExecutionError +from tests.support.mock import patch + +pytestmark = [ + pytest.mark.skip_unless_on_darwin, +] + + +@pytest.fixture +def zero_date(): + return datetime.fromtimestamp(0).strftime("%Y-%m-%d %H:%M:%S") + + +def test_get_account_created(zero_date): + with patch.object(mac_shadow, "_get_account_policy_data_value", return_value="0"): + result = mac_shadow.get_account_created("junk") + assert result == zero_date + + +def test_get_account_created_no_value(): + with patch.object( + mac_shadow, + "_get_account_policy_data_value", + side_effect=CommandExecutionError("Value not found: creationTime"), + ): + result = mac_shadow.get_account_created("junk") + expected = "0" + assert result == expected + + +def test_get_account_created_error(): + with patch.object( + mac_shadow, + "_get_account_policy_data_value", + side_effect=CommandExecutionError("Unknown error: something happened"), + ), pytest.raises(CommandExecutionError): + mac_shadow.get_account_created("junk") + + +def test_get_last_change(zero_date): + with patch.object(mac_shadow, "_get_account_policy_data_value", return_value="0"): + result = mac_shadow.get_last_change("junk") + assert result == zero_date + + +def test_get_last_change_no_value(): + with patch.object( + mac_shadow, + "_get_account_policy_data_value", + side_effect=CommandExecutionError("Value not found: creationTime"), + ): + result = mac_shadow.get_last_change("junk") + expected = "0" + assert result == expected + + +def test_get_last_change_error(): + with patch.object( + mac_shadow, + "_get_account_policy_data_value", + side_effect=CommandExecutionError("Unknown error: something happened"), + ), pytest.raises(CommandExecutionError): + mac_shadow.get_last_change("junk") + + +def test_login_failed_count(): + with patch.object(mac_shadow, "_get_account_policy_data_value", return_value="0"): + result = mac_shadow.get_login_failed_count("junk") + expected = "0" + assert result == expected + + +def test_get_login_failed_count_no_value(): + with patch.object( + mac_shadow, + "_get_account_policy_data_value", + side_effect=CommandExecutionError("Value not found: creationTime"), + ): + result = mac_shadow.get_login_failed_count("junk") + expected = "0" + assert result == expected + + +def test_get_login_failed_count_error(): + with patch.object( + mac_shadow, + "_get_account_policy_data_value", + side_effect=CommandExecutionError("Unknown error: something happened"), + ), pytest.raises(CommandExecutionError): + mac_shadow.get_login_failed_count("junk") + + +def test_login_failed_last(zero_date): + with patch.object(mac_shadow, "_get_account_policy_data_value", return_value="0"): + result = mac_shadow.get_login_failed_last("junk") + assert result == zero_date + + +def test_get_login_failed_last_no_value(): + with patch.object( + mac_shadow, + "_get_account_policy_data_value", + side_effect=CommandExecutionError("Value not found: creationTime"), + ): + result = mac_shadow.get_login_failed_last("junk") + expected = "0" + assert result == expected + + +def test_get_login_failed_last_error(): + with patch.object( + mac_shadow, + "_get_account_policy_data_value", + side_effect=CommandExecutionError("Unknown error: something happened"), + ), pytest.raises(CommandExecutionError): + mac_shadow.get_login_failed_last("junk") diff --git a/tests/pytests/unit/modules/win_lgpo/test_adv_audit.py b/tests/pytests/unit/modules/win_lgpo/test_adv_audit.py index 1f8e83eeab3..c982ff6abb0 100644 --- a/tests/pytests/unit/modules/win_lgpo/test_adv_audit.py +++ b/tests/pytests/unit/modules/win_lgpo/test_adv_audit.py @@ -1,3 +1,5 @@ +import logging + import pytest import salt.modules.win_file as win_file @@ -158,11 +160,14 @@ def test_set_value_log_messages(caplog): mock_set_file_data = MagicMock(return_value=True) mock_set_pol_data = MagicMock(return_value=False) mock_context = {"lgpo.adv_audit_data": {"test_option": "test_value"}} - with patch.object( - win_lgpo, "_set_advaudit_file_data", mock_set_file_data - ), patch.object(win_lgpo, "_set_advaudit_pol_data", mock_set_pol_data), patch.dict( - win_lgpo.__context__, mock_context - ): - win_lgpo._set_advaudit_value("test_option", None) - assert "Failed to apply audit setting:" in caplog.text + with caplog.at_level(logging.DEBUG): + with patch.object( + win_lgpo, "_set_advaudit_file_data", mock_set_file_data + ), patch.object( + win_lgpo, "_set_advaudit_pol_data", mock_set_pol_data + ), patch.dict( + win_lgpo.__context__, mock_context + ): + win_lgpo._set_advaudit_value("test_option", None) + assert "Failed to apply audit setting:" in caplog.text assert "LGPO: Removing Advanced Audit data:" in caplog.text diff --git a/tests/pytests/unit/modules/win_lgpo/test_secedit.py b/tests/pytests/unit/modules/win_lgpo/test_secedit.py index 47a39fb8250..3a5228f465d 100644 --- a/tests/pytests/unit/modules/win_lgpo/test_secedit.py +++ b/tests/pytests/unit/modules/win_lgpo/test_secedit.py @@ -1,3 +1,5 @@ +import logging + import pytest import salt.modules.cmdmod as cmd @@ -69,15 +71,17 @@ def test_write_secedit_data_import_fail(caplog): patch_cmd_retcode = patch.dict( win_lgpo.__salt__, {"cmd.retcode": MagicMock(return_value=1)} ) - with patch_cmd_retcode: - assert win_lgpo._write_secedit_data("spongebob") is False - assert "Secedit failed to import template data" in caplog.text + with caplog.at_level(logging.DEBUG): + with patch_cmd_retcode: + assert win_lgpo._write_secedit_data("spongebob") is False + assert "Secedit failed to import template data" in caplog.text def test_write_secedit_data_configure_fail(caplog): patch_cmd_retcode = patch.dict( win_lgpo.__salt__, {"cmd.retcode": MagicMock(side_effect=[0, 1])} ) - with patch_cmd_retcode: - assert win_lgpo._write_secedit_data("spongebob") is False - assert "Secedit failed to apply security database" in caplog.text + with caplog.at_level(logging.DEBUG): + with patch_cmd_retcode: + assert win_lgpo._write_secedit_data("spongebob") is False + assert "Secedit failed to apply security database" in caplog.text diff --git a/tests/pytests/unit/state/test_state_compiler.py b/tests/pytests/unit/state/test_state_compiler.py index 352c51d2288..e8a1fa71fd4 100644 --- a/tests/pytests/unit/state/test_state_compiler.py +++ b/tests/pytests/unit/state/test_state_compiler.py @@ -42,10 +42,11 @@ def test_format_log_list(caplog): """ Test running format_log when ret is not a dictionary """ - ret = ["test1", "test2"] - salt.state.format_log(ret) - assert "INFO" in caplog.text - assert f"{ret}" in caplog.text + with caplog.at_level(logging.INFO): + ret = ["test1", "test2"] + salt.state.format_log(ret) + assert "INFO" in caplog.text + assert f"{ret}" in caplog.text def test_render_error_on_invalid_requisite(minion_opts): diff --git a/tests/pytests/unit/states/test_win_task.py b/tests/pytests/unit/states/test_win_task.py new file mode 100644 index 00000000000..73daffc6eeb --- /dev/null +++ b/tests/pytests/unit/states/test_win_task.py @@ -0,0 +1,681 @@ +# https://msdn.microsoft.com/en-us/library/windows/desktop/aa383608(v=vs.85).aspx + +import pytest + +import salt.modules.win_task +import salt.states.win_task as win_task +import salt.utils.platform +from tests.support.mock import MagicMock, patch + +pytestmark = [ + pytest.mark.windows_whitelisted, + pytest.mark.skip_unless_on_windows, + pytest.mark.destructive_test, +] + + +@pytest.fixture +def configure_loader_modules(): + return {win_task: {}} + + +def test_present(): + kwargs = { + "action_type": "Execute", + "cmd": "del /Q /S C:\\\\Temp", + "trigger_type": "Once", + "start_data": "2019-05-14", + "start_time": "01:00 pm", + } + + ret = {"result": False} + try: + with patch.dict( + win_task.__salt__, + { + "task.list_tasks": salt.modules.win_task.list_tasks, + "task.info": salt.modules.win_task.info, + "task.create_task": salt.modules.win_task.create_task, + }, + ), patch.dict(win_task.__opts__, {"test": False}), patch.dict( + win_task.__grains__, {"osversion": "7.1"} + ): + + ret = win_task.present(name="salt", location="", force=True, **kwargs) + finally: + try: + salt.modules.win_task.delete_task(name="salt", location="") + finally: + pass + + assert ret["result"] + + +def test_absent(): + with patch.dict( + win_task.__salt__, + { + "task.list_tasks": salt.modules.win_task.list_tasks, + "task.info": salt.modules.win_task.info, + "task.delete_task": salt.modules.win_task.delete_task, + }, + ), patch.dict(win_task.__opts__, {"test": False}): + ret = win_task.absent("salt", "") + + assert ret["result"] + + kwargs = { + "action_type": "Execute", + "cmd": "del /Q /S C:\\\\Temp", + "trigger_type": "Once", + "start_data": "2019-05-14", + "start_time": "01:00 pm", + } + + try: + with patch.dict( + win_task.__salt__, + { + "task.list_tasks": salt.modules.win_task.list_tasks, + "task.info": salt.modules.win_task.info, + "task.create_task": salt.modules.win_task.create_task, + }, + ), patch.dict(win_task.__opts__, {"test": False}), patch.dict( + win_task.__grains__, {"osversion": "7.1"} + ): + + win_task.present(name="salt", location="", force=True, **kwargs) + finally: + try: + with patch.dict( + win_task.__salt__, + { + "task.list_tasks": salt.modules.win_task.list_tasks, + "task.info": salt.modules.win_task.info, + "task.delete_task": salt.modules.win_task.delete_task, + }, + ), patch.dict(win_task.__opts__, {"test": False}): + ret = win_task.absent("salt", "") + finally: + pass + + assert ret["result"] + + +def test__get_arguments(): + kwargs = {"salt": True, "cat": "nice", "idk": 404} + + true_ret = {"salt": True, "cat": "nice", "fat": True, "idk": 404} + + ret = win_task._get_arguments( + kwargs, ["cat"], {"nice": ["idk"], "sad": ["why"]}, {"fat": True, "salt": None} + ) + + assert ret == true_ret + + +def test__get_task_state_prediction(): + state = { + "task_found": True, + "location_valid": True, + "task_info": { + "conditions": { + "ac_only": True, + "run_if_idle": False, + "run_if_network": False, + "start_when_available": False, + }, + "actions": [{"cmd": "del /Q /S C:\\\\Temp", "action_type": "Execute"}], + "triggers": [ + { + "delay": False, + "execution_time_limit": "3 days", + "trigger_type": "OnSessionChange", + "start_date": "2019-05-14", + "enabled": True, + "start_time": "13:00:00", + } + ], + "settings": { + "delete_after": False, + "multiple_instances": "No New Instance", + "execution_time_limit": "3 days", + "allow_demand_start": True, + "restart_interval": False, + "stop_if_on_batteries": True, + "force_stop": True, + "wake_to_run": False, + }, + }, + } + + task_info = { + "conditions": { + "ac_only": True, + "run_if_idle": False, + "run_if_network": False, + "start_when_available": False, + }, + "trigger": { + "end_date": None, + "execution_time_limit": "3 days", + "state_change": "SessionUnlock", + "random_delay": False, + "end_time": "00:00:00", + "start_date": "2019-05-14", + "repeat_duration": None, + "start_time": "01:00 pm", + "repeat_interval": None, + "delay": False, + "trigger_enabled": True, + "trigger_type": "OnSessionChange", + "repeat_stop_at_duration_end": False, + }, + "action": { + "start_in": "", + "cmd": "del /Q /S C:\\\\Temp", + "arguments": "", + "action_type": "Execute", + }, + "settings": { + "delete_after": False, + "multiple_instances": "No New Instance", + "execution_time_limit": "3 days", + "allow_demand_start": True, + "restart_interval": False, + "stop_if_on_batteries": True, + "force_stop": True, + "wake_to_run": False, + }, + } + + prediction = win_task._get_task_state_prediction(state, task_info) + assert state == prediction + + +# The following tests check if the state prediction is correct. A lot of tests +# might look the same but under the hood a lot of checks are happening. The +# Triggers Test does not test Once or Event +def test_daily(): + kwargs = { + "action_type": "Execute", + "cmd": "del /Q /S C:\\\\Temp", + "trigger_type": "Daily", + "start_date": "2019-05-14", + "start_time": "01:00 pm", + "days_interval": 101, + } + + info = { + "triggers": [ + { + "random_delay": False, + "trigger_type": "Daily", + "execution_time_limit": "3 days", + "start_time": "13:00:00", + "enabled": True, + "start_date": "2019-05-14", + } + ], + "actions": [{"cmd": "del /Q /S C:\\\\Temp", "action_type": "Execute"}], + "conditions": { + "start_when_available": False, + "run_if_network": False, + "ac_only": True, + "run_if_idle": False, + }, + "settings": { + "wake_to_run": False, + "allow_demand_start": True, + "multiple_instances": "No New Instance", + "execution_time_limit": "3 days", + "force_stop": True, + "delete_after": False, + "stop_if_on_batteries": True, + "restart_interval": False, + }, + } + + with patch.dict( + win_task.__salt__, + { + "task.list_tasks": MagicMock(side_effect=[["salt"]] * 2), + "task.info": MagicMock(side_effect=[info]), + }, + ), patch.dict(win_task.__opts__, {"test": True}), patch.dict( + win_task.__grains__, {"osversion": "7.1"} + ): + ret = win_task.present(name="salt", location="", force=True, **kwargs) + assert ret["result"] + + +def test_weekly(): + kwargs = { + "action_type": "Execute", + "cmd": "del /Q /S C:\\\\Temp", + "trigger_type": "Weekly", + "start_date": "2019-05-14", + "start_time": "01:00 pm", + "days_of_week": ["Monday", "Wednesday", "Friday"], + "weeks_interval": 1, + } + + info = { + "triggers": [ + { + "start_date": "2019-05-14", + "execution_time_limit": "3 days", + "random_delay": False, + "enabled": True, + "start_time": "13:00:00", + "trigger_type": "Weekly", + } + ], + "actions": [{"cmd": "del /Q /S C:\\\\Temp", "action_type": "Execute"}], + "conditions": { + "start_when_available": False, + "run_if_idle": False, + "run_if_network": False, + "ac_only": True, + }, + "settings": { + "allow_demand_start": True, + "wake_to_run": False, + "execution_time_limit": "3 days", + "force_stop": True, + "multiple_instances": "No New Instance", + "stop_if_on_batteries": True, + "restart_interval": False, + "delete_after": False, + }, + } + + with patch.dict( + win_task.__salt__, + { + "task.list_tasks": MagicMock(side_effect=[["salt"]] * 2), + "task.info": MagicMock(side_effect=[info]), + }, + ), patch.dict(win_task.__opts__, {"test": True}), patch.dict( + win_task.__grains__, {"osversion": "7.1"} + ): + ret = win_task.present(name="salt", location="", force=True, **kwargs) + assert ret["result"] + + +def test_monthly(): + kwargs = { + "action_type": "Execute", + "cmd": "del /Q /S C:\\\\Temp", + "trigger_type": "Monthly", + "start_date": "2019-05-14", + "start_time": "01:00 pm", + "months_of_year": ["January", "July"], + "days_of_month": [6, 16, 26], + "last_day_of_month": True, + } + + info = { + "triggers": [ + { + "start_date": "2019-05-14", + "random_delay": False, + "trigger_type": "Monthly", + "execution_time_limit": "3 days", + "start_time": "13:00:00", + "enabled": True, + } + ], + "actions": [{"cmd": "del /Q /S C:\\\\Temp", "action_type": "Execute"}], + "conditions": { + "run_if_idle": False, + "run_if_network": False, + "start_when_available": False, + "ac_only": True, + }, + "settings": { + "force_stop": True, + "allow_demand_start": True, + "delete_after": False, + "multiple_instances": "No New Instance", + "execution_time_limit": "3 days", + "stop_if_on_batteries": True, + "restart_interval": False, + "wake_to_run": False, + }, + } + + with patch.dict( + win_task.__salt__, + { + "task.list_tasks": MagicMock(side_effect=[["salt"]] * 2), + "task.info": MagicMock(side_effect=[info]), + }, + ), patch.dict(win_task.__opts__, {"test": True}), patch.dict( + win_task.__grains__, {"osversion": "7.1"} + ): + ret = win_task.present(name="salt", location="", force=True, **kwargs) + assert ret["result"] + + +def test_monthly_day(): + kwargs = { + "action_type": "Execute", + "cmd": "del /Q /S C:\\\\Temp", + "trigger_type": "MonthlyDay", + "start_date": "2019-05-14", + "start_time": "01:00 pm", + "months_of_year": ["January", "July"], + "weeks_of_month": ["First", "Third"], + "last_week_of_month": True, + "days_of_week": ["Monday", "Wednesday", "Friday"], + } + + info = { + "triggers": [ + { + "start_date": "2019-05-14", + "random_delay": False, + "trigger_type": "MonthlyDay", + "execution_time_limit": "3 days", + "start_time": "13:00:00", + "enabled": True, + } + ], + "actions": [{"cmd": "del /Q /S C:\\\\Temp", "action_type": "Execute"}], + "conditions": { + "run_if_idle": False, + "run_if_network": False, + "start_when_available": False, + "ac_only": True, + }, + "settings": { + "force_stop": True, + "allow_demand_start": True, + "delete_after": False, + "multiple_instances": "No New Instance", + "execution_time_limit": "3 days", + "stop_if_on_batteries": True, + "restart_interval": False, + "wake_to_run": False, + }, + } + + with patch.dict( + win_task.__salt__, + { + "task.list_tasks": MagicMock(side_effect=[["salt"]] * 2), + "task.info": MagicMock(side_effect=[info]), + }, + ), patch.dict(win_task.__opts__, {"test": True}), patch.dict( + win_task.__grains__, {"osversion": "7.1"} + ): + + ret = win_task.present(name="salt", location="", force=True, **kwargs) + assert ret["result"] + + +def test_on_idle(): + kwargs = { + "action_type": "Execute", + "cmd": "del /Q /S C:\\\\Temp", + "trigger_type": "OnIdle", + "start_date": "2019-05-14", + "start_time": "01:00 pm", + } + + info = { + "triggers": [ + { + "start_date": "2019-05-14", + "random_delay": False, + "trigger_type": "OnIdle", + "execution_time_limit": "3 days", + "start_time": "13:00:00", + "enabled": True, + } + ], + "actions": [{"cmd": "del /Q /S C:\\\\Temp", "action_type": "Execute"}], + "conditions": { + "run_if_idle": False, + "run_if_network": False, + "start_when_available": False, + "ac_only": True, + }, + "settings": { + "force_stop": True, + "allow_demand_start": True, + "delete_after": False, + "multiple_instances": "No New Instance", + "execution_time_limit": "3 days", + "stop_if_on_batteries": True, + "restart_interval": False, + "wake_to_run": False, + }, + } + + with patch.dict( + win_task.__salt__, + { + "task.list_tasks": MagicMock(side_effect=[["salt"]] * 2), + "task.info": MagicMock(side_effect=[info]), + }, + ), patch.dict(win_task.__opts__, {"test": True}), patch.dict( + win_task.__grains__, {"osversion": "7.1"} + ): + ret = win_task.present(name="salt", location="", force=True, **kwargs) + assert ret["result"] + + +def test_on_task_creation(): + kwargs = { + "action_type": "Execute", + "cmd": "del /Q /S C:\\\\Temp", + "trigger_type": "OnTaskCreation", + "start_date": "2019-05-14", + "start_time": "01:00 pm", + } + + info = { + "triggers": [ + { + "start_date": "2019-05-14", + "random_delay": False, + "trigger_type": "OnTaskCreation", + "execution_time_limit": "3 days", + "start_time": "13:00:00", + "enabled": True, + } + ], + "actions": [{"cmd": "del /Q /S C:\\\\Temp", "action_type": "Execute"}], + "conditions": { + "run_if_idle": False, + "run_if_network": False, + "start_when_available": False, + "ac_only": True, + }, + "settings": { + "force_stop": True, + "allow_demand_start": True, + "delete_after": False, + "multiple_instances": "No New Instance", + "execution_time_limit": "3 days", + "stop_if_on_batteries": True, + "restart_interval": False, + "wake_to_run": False, + }, + } + + with patch.dict( + win_task.__salt__, + { + "task.list_tasks": MagicMock(side_effect=[["salt"]] * 2), + "task.info": MagicMock(side_effect=[info]), + }, + ), patch.dict(win_task.__opts__, {"test": True}), patch.dict( + win_task.__grains__, {"osversion": "7.1"} + ): + + ret = win_task.present(name="salt", location="", force=True, **kwargs) + assert ret["result"] + + +def test_on_boot(): + kwargs = { + "action_type": "Execute", + "cmd": "del /Q /S C:\\\\Temp", + "trigger_type": "OnBoot", + "start_date": "2019-05-14", + "start_time": "01:00 pm", + } + + info = { + "triggers": [ + { + "start_date": "2019-05-14", + "random_delay": False, + "trigger_type": "OnBoot", + "execution_time_limit": "3 days", + "start_time": "13:00:00", + "enabled": True, + "delay": False, + } + ], + "actions": [{"cmd": "del /Q /S C:\\\\Temp", "action_type": "Execute"}], + "conditions": { + "run_if_idle": False, + "run_if_network": False, + "start_when_available": False, + "ac_only": True, + }, + "settings": { + "force_stop": True, + "allow_demand_start": True, + "delete_after": False, + "multiple_instances": "No New Instance", + "execution_time_limit": "3 days", + "stop_if_on_batteries": True, + "restart_interval": False, + "wake_to_run": False, + }, + } + + with patch.dict( + win_task.__salt__, + { + "task.list_tasks": MagicMock(side_effect=[["salt"]] * 2), + "task.info": MagicMock(side_effect=[info]), + }, + ), patch.dict(win_task.__opts__, {"test": True}), patch.dict( + win_task.__grains__, {"osversion": "7.1"} + ): + + ret = win_task.present(name="salt", location="", force=True, **kwargs) + assert ret["result"] + + +def test_on_logon(): + kwargs = { + "action_type": "Execute", + "cmd": "del /Q /S C:\\\\Temp", + "trigger_type": "OnLogon", + "start_date": "2019-05-14", + "start_time": "01:00 pm", + } + + info = { + "triggers": [ + { + "start_date": "2019-05-14", + "random_delay": False, + "trigger_type": "OnLogon", + "execution_time_limit": "3 days", + "start_time": "13:00:00", + "enabled": True, + } + ], + "actions": [{"cmd": "del /Q /S C:\\\\Temp", "action_type": "Execute"}], + "conditions": { + "run_if_idle": False, + "run_if_network": False, + "start_when_available": False, + "ac_only": True, + }, + "settings": { + "force_stop": True, + "allow_demand_start": True, + "delete_after": False, + "multiple_instances": "No New Instance", + "execution_time_limit": "3 days", + "stop_if_on_batteries": True, + "restart_interval": False, + "wake_to_run": False, + }, + } + + with patch.dict( + win_task.__salt__, + { + "task.list_tasks": MagicMock(side_effect=[["salt"]] * 2), + "task.info": MagicMock(side_effect=[info]), + }, + ), patch.dict(win_task.__opts__, {"test": True}), patch.dict( + win_task.__grains__, {"osversion": "7.1"} + ): + + ret = win_task.present(name="salt", location="", force=True, **kwargs) + assert ret["result"] + + +def test_on_session_change(): + kwargs = { + "action_type": "Execute", + "cmd": "del /Q /S C:\\\\Temp", + "trigger_type": "OnSessionChange", + "start_date": "2019-05-14", + "start_time": "01:00 pm", + "state_change": "SessionUnlock", + } + + info = { + "actions": [{"cmd": "del /Q /S C:\\\\Temp", "action_type": "Execute"}], + "settings": { + "delete_after": False, + "execution_time_limit": "3 days", + "wake_to_run": False, + "force_stop": True, + "multiple_instances": "No New Instance", + "stop_if_on_batteries": True, + "restart_interval": False, + "allow_demand_start": True, + }, + "triggers": [ + { + "trigger_type": "OnSessionChange", + "execution_time_limit": "3 days", + "delay": False, + "enabled": True, + "start_date": "2019-05-14", + "start_time": "13:00:00", + } + ], + "conditions": { + "run_if_idle": False, + "ac_only": True, + "run_if_network": False, + "start_when_available": False, + }, + } + + with patch.dict( + win_task.__salt__, + { + "task.list_tasks": MagicMock(side_effect=[["salt"]] * 2), + "task.info": MagicMock(side_effect=[info]), + }, + ), patch.dict(win_task.__opts__, {"test": True}), patch.dict( + win_task.__grains__, {"osversion": "7.1"} + ): + ret = win_task.present(name="salt", location="", force=True, **kwargs) + + assert ret["result"] diff --git a/tests/pytests/unit/test_client.py b/tests/pytests/unit/test_client.py index 48a21873697..fde31f459fb 100644 --- a/tests/pytests/unit/test_client.py +++ b/tests/pytests/unit/test_client.py @@ -247,3 +247,44 @@ def test_pub_win32(salt_master_factory): "test.ping", tgt_type="nodegroup", ) + + +def test_invalid_event_tag_65727(master_opts, caplog): + """ + LocalClient.get_iter_returns handles non return event tags. + """ + minions = () + jid = "0815" + raw_return = {"id": "fake-id", "jid": jid, "data": "", "return": "fake-return"} + expected_return = {"fake-id": {"ret": "fake-return"}} + + def returns_iter(): + # Invalid return + yield { + "tag": "salt/job/0815/return/", + "data": { + "return": "fpp", + "id": "fake-id", + }, + } + # Valid return + yield { + "tag": "salt/job/0815/ret/", + "data": { + "return": "fpp", + "id": "fake-id", + }, + } + + with client.LocalClient(mopts=master_opts) as local_client: + # Returning a truthy value, the real method returns a salt returner but it's not used. + local_client.returns_for_job = MagicMock(return_value=True) + # Mock iter returns, we'll return one invalid and one valid return event. + local_client.get_returns_no_block = MagicMock(return_value=returns_iter()) + with caplog.at_level(logging.DEBUG): + # Validate we don't choke on the bad return, the method returns a + # valid respons and the invalid event tag is getting logged to + # debug. + for ret in local_client.get_iter_returns(jid, {"fake-id"}): + assert ret == {"fake-id": {"ret": "fpp"}} + assert "Skipping non return event: salt/job/0815/return/" in caplog.text diff --git a/tests/pytests/unit/channel/test_server.py b/tests/pytests/unit/test_crypt.py similarity index 80% rename from tests/pytests/unit/channel/test_server.py rename to tests/pytests/unit/test_crypt.py index d50ae416026..d6fd1f3239a 100644 --- a/tests/pytests/unit/channel/test_server.py +++ b/tests/pytests/unit/test_crypt.py @@ -1,6 +1,6 @@ import pytest -import salt.channel.server as server +import salt.crypt as crypt @pytest.fixture @@ -22,8 +22,7 @@ def key_data(): def test__clean_key(key_data, linesep): tst_key = linesep.join(key_data) chk_key = "\n".join(key_data) - clean_func = server.ReqServerChannel._clean_key - assert clean_func(tst_key) == clean_func(chk_key) + assert crypt.clean_key(tst_key) == crypt.clean_key(chk_key) @pytest.mark.parametrize("linesep", ["\r\n", "\r", "\n"]) @@ -31,5 +30,4 @@ def test__clean_key_mismatch(key_data, linesep): tst_key = linesep.join(key_data) tst_key = tst_key.replace("5", "4") chk_key = "\n".join(key_data) - clean_func = server.ReqServerChannel._clean_key - assert clean_func(tst_key) != clean_func(chk_key) + assert crypt.clean_key(tst_key) != crypt.clean_key(chk_key) diff --git a/tests/pytests/unit/test_fileclient.py b/tests/pytests/unit/test_fileclient.py new file mode 100644 index 00000000000..d11f5ac2521 --- /dev/null +++ b/tests/pytests/unit/test_fileclient.py @@ -0,0 +1,34 @@ +""" +Unit tests for salt.fileclient +""" +import salt.config +import salt.fileclient as fileclient +from tests.support.mock import MagicMock, patch + + +def test_fsclient_master_no_fs_update(master_opts): + """ + Test that an FSClient spawned from the master does not cause fileserver + backends to be refreshed on instantiation. The master already has the + maintenance thread for that. + """ + overrides = {"file_client": "local"} + opts = salt.config.apply_master_config(overrides, master_opts) + fileserver = MagicMock() + with patch("salt.fileserver.Fileserver", fileserver): + client = fileclient.FSClient(opts) + assert client.channel.fs.update.call_count == 0 + + +def test_fsclient_masterless_fs_update(minion_opts): + """ + Test that an FSClient spawned from a masterless run refreshes the + fileserver backends. This is necessary to ensure that a masterless run + can access any configured gitfs remotes. + """ + overrides = {"file_client": "local"} + opts = salt.config.apply_minion_config(overrides, minion_opts) + fileserver = MagicMock() + with patch("salt.fileserver.Fileserver", fileserver): + client = fileclient.FSClient(opts) + assert client.channel.fs.update.call_count == 1 diff --git a/tests/pytests/unit/test_minion.py b/tests/pytests/unit/test_minion.py index 00b490def8e..9523a81a92f 100644 --- a/tests/pytests/unit/test_minion.py +++ b/tests/pytests/unit/test_minion.py @@ -22,6 +22,31 @@ from tests.support.mock import MagicMock, patch log = logging.getLogger(__name__) +@pytest.fixture +def connect_master_mock(): + class ConnectMasterMock: + """ + Mock connect master call. + + The first call will raise an exception stored on the exc attribute. + Subsequent calls will return True. + """ + + def __init__(self): + self.calls = 0 + self.exc = Exception + + @tornado.gen.coroutine + def __call__(self, *args, **kwargs): + self.calls += 1 + if self.calls == 1: + raise self.exc() + else: + return True + + return ConnectMasterMock() + + def test_minion_load_grains_false(minion_opts): """ Minion does not generate grains when load_grains is False @@ -1126,3 +1151,55 @@ def test_load_args_and_kwargs(minion_opts): _args = [{"max_sleep": 40, "__kwarg__": True}] with pytest.raises(salt.exceptions.SaltInvocationError): ret = salt.minion.load_args_and_kwargs(test_mod.rand_sleep, _args) + + +async def test_connect_master_salt_client_error(minion_opts, connect_master_mock): + """ + Ensure minion's destory method is called on an salt client error while connecting to master. + """ + minion_opts["acceptance_wait_time"] = 0 + mm = salt.minion.MinionManager(minion_opts) + minion = salt.minion.Minion(minion_opts) + + connect_master_mock.exc = SaltClientError + minion.connect_master = connect_master_mock + minion.destroy = MagicMock() + await mm._connect_minion(minion) + minion.destroy.assert_called_once() + + # The first call raised an error which caused minion.destroy to get called, + # the second call is a success. + assert minion.connect_master.calls == 2 + + +async def test_connect_master_unresolveable_error(minion_opts, connect_master_mock): + """ + Ensure minion's destory method is called on an unresolvable while connecting to master. + """ + mm = salt.minion.MinionManager(minion_opts) + minion = salt.minion.Minion(minion_opts) + connect_master_mock.exc = SaltMasterUnresolvableError + minion.connect_master = connect_master_mock + minion.destroy = MagicMock() + await mm._connect_minion(minion) + minion.destroy.assert_called_once() + + # Unresolvable errors break out of the loop. + assert minion.connect_master.calls == 1 + + +async def test_connect_master_general_exception_error(minion_opts, connect_master_mock): + """ + Ensure minion's destory method is called on an un-handled exception while connecting to master. + """ + mm = salt.minion.MinionManager(minion_opts) + minion = salt.minion.Minion(minion_opts) + connect_master_mock.exc = SaltClientError + minion.connect_master = connect_master_mock + minion.destroy = MagicMock() + await mm._connect_minion(minion) + minion.destroy.assert_called_once() + + # The first call raised an error which caused minion.destroy to get called, + # the second call is a success. + assert minion.connect_master.calls == 2 diff --git a/tests/pytests/unit/channel/test_request_channel.py b/tests/pytests/unit/test_request_channel.py similarity index 99% rename from tests/pytests/unit/channel/test_request_channel.py rename to tests/pytests/unit/test_request_channel.py index 5cddb668bc6..b8957f35e30 100644 --- a/tests/pytests/unit/channel/test_request_channel.py +++ b/tests/pytests/unit/test_request_channel.py @@ -996,7 +996,7 @@ async def test_req_serv_auth_v1(minion_opts, master_opts, pki_dir): with salt.utils.files.fopen( str(pki_dir.joinpath("minion", "minion.pub")), "r" ) as fp: - pub_key = fp.read() + pub_key = salt.crypt.clean_key(fp.read()) load = { "cmd": "_auth", @@ -1054,7 +1054,7 @@ async def test_req_serv_auth_v2(minion_opts, master_opts, pki_dir): with salt.utils.files.fopen( str(pki_dir.joinpath("minion", "minion.pub")), "r" ) as fp: - pub_key = fp.read() + pub_key = salt.crypt.clean_key(fp.read()) load = { "cmd": "_auth", diff --git a/tests/pytests/unit/transport/test_zeromq.py b/tests/pytests/unit/transport/test_zeromq.py index f956e49818a..01066334c94 100644 --- a/tests/pytests/unit/transport/test_zeromq.py +++ b/tests/pytests/unit/transport/test_zeromq.py @@ -2,10 +2,15 @@ import logging import msgpack import pytest +import zmq.eventloop.future import salt.config import salt.transport.base import salt.transport.zeromq +import salt.utils.platform +import salt.utils.process +import salt.utils.stringutils +from tests.support.mock import AsyncMock, MagicMock log = logging.getLogger(__name__) @@ -53,6 +58,39 @@ async def test_client_timeout_msg(minion_opts): client.close() +async def test_client_send_recv_on_cancelled_error(minion_opts): + client = salt.transport.zeromq.AsyncReqMessageClient( + minion_opts, "tcp://127.0.0.1:4506" + ) + + mock_future = MagicMock(**{"done.return_value": True}) + + try: + client.socket = AsyncMock() + client.socket.recv.side_effect = zmq.eventloop.future.CancelledError + await client._send_recv({"meh": "bah"}, mock_future) + + mock_future.set_exception.assert_not_called() + finally: + client.close() + + +async def test_client_send_recv_on_exception(minion_opts): + client = salt.transport.zeromq.AsyncReqMessageClient( + minion_opts, "tcp://127.0.0.1:4506" + ) + + mock_future = MagicMock(**{"done.return_value": True}) + + try: + client.socket = None + await client._send_recv({"meh": "bah"}, mock_future) + + mock_future.set_exception.assert_not_called() + finally: + client.close() + + def test_pub_client_init(minion_opts, io_loop): minion_opts["id"] = "minion" minion_opts["__role"] = "syndic" diff --git a/tests/pytests/unit/utils/test_rsax931.py b/tests/pytests/unit/utils/test_rsax931.py index a9f084fccf2..28585f04415 100644 --- a/tests/pytests/unit/utils/test_rsax931.py +++ b/tests/pytests/unit/utils/test_rsax931.py @@ -1,7 +1,6 @@ """ Test the RSA ANSI X9.31 signer and verifier """ - import ctypes import ctypes.util import fnmatch @@ -215,6 +214,47 @@ def test_find_libcrypto_darwin_catalina(): assert "/usr/lib/libcrypto.44.dylib" == lib_path +@pytest.mark.skip_unless_on_darwin +def test_find_libcrypto_darwin_pip_install(): + """ + Test _find_libcrypto on a macOS host where there salt has been installed + into an existing python or virtual environment. + """ + bin_path = "/Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10" + expected = "/Library/Frameworks/Python.framework/Versions/3.10/lib/libcrypto.dylib" + glob_effect = ([], [], ["yay"], [], [], [], []) + with patch("salt.utils.platform.is_darwin", lambda: True), patch( + "sys.executable", bin_path + ), patch("os.path.islink", return_value=False), patch.object( + glob, "glob", side_effect=glob_effect + ) as mock_glob: + lib_path = _find_libcrypto() + assert lib_path == "yay" + mock_glob.assert_any_call(expected) + + +@pytest.mark.skip_unless_on_darwin +def test_find_libcrypto_darwin_pip_install_venv(): + """ + Test _find_libcrypto on a macOS host where there salt has been installed + into an existing python or virtual environment. + """ + src_path = "/Library/Frameworks/Python.framework/Versions/3.10/bin/python3.10" + lnk_path = "/Users/bill/src/salt/venv/bin/python" + expected = "/Library/Frameworks/Python.framework/Versions/3.10/lib/libcrypto.dylib" + glob_effect = ([], [], ["yay"], [], [], [], []) + with patch("salt.utils.platform.is_darwin", lambda: True), patch( + "sys.executable", lnk_path + ), patch("os.path.islink", return_value=True), patch( + "os.path.realpath", return_value=src_path + ), patch.object( + glob, "glob", side_effect=glob_effect + ) as mock_glob: + lib_path = _find_libcrypto() + assert lib_path == "yay" + mock_glob.assert_any_call(expected) + + def test_find_libcrypto_darwin_bigsur_packaged(): """ Test _find_libcrypto on a Darwin-like macOS host where there isn't a diff --git a/tests/unit/states/test_pip_state.py b/tests/unit/states/test_pip_state.py index dbac7b926e6..444c332776d 100644 --- a/tests/unit/states/test_pip_state.py +++ b/tests/unit/states/test_pip_state.py @@ -1,11 +1,5 @@ -""" -tests.unit.states.pip_test -~~~~~~~~~~~~~~~~~~~~~~~~~~ -""" - import logging import os -import subprocess import sys import pytest @@ -13,27 +7,18 @@ import pytest import salt.states.pip_state as pip_state import salt.utils.path import salt.version -from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES -from tests.support.helpers import VirtualEnv, dedent from tests.support.mixins import LoaderModuleMockMixin, SaltReturnAssertsMixin from tests.support.mock import MagicMock, patch from tests.support.runtests import RUNTIME_VARS from tests.support.unit import TestCase -try: - import pip - - HAS_PIP = True -except ImportError: - HAS_PIP = False - +pip = pytest.importorskip( + "pip", reason="The 'pip' library is not importable(installed system-wide)" +) log = logging.getLogger(__name__) -@pytest.mark.skipif( - not HAS_PIP, reason="The 'pip' library is not importable(installed system-wide)" -) class PipStateTest(TestCase, SaltReturnAssertsMixin, LoaderModuleMockMixin): def setup_loader_modules(self): return { @@ -422,78 +407,3 @@ class PipStateUtilsTest(TestCase): mock_modules.pop("pip", None) with patch("sys.modules", mock_modules): pip_state.purge_pip() - - -@pytest.mark.skip_if_binaries_missing(*KNOWN_BINARY_NAMES, check_all=False) -@pytest.mark.requires_network -class PipStateInstallationErrorTest(TestCase): - @pytest.mark.slow_test - def test_importable_installation_error(self): - extra_requirements = [] - for name, version in salt.version.dependency_information(): - if name in ["PyYAML", "packaging", "looseversion"]: - extra_requirements.append(f"{name}=={version}") - failures = {} - pip_version_requirements = [ - # Latest pip 18 - "<19.0", - # Latest pip 19 - "<20.0", - # Latest pip 20 - "<21.0", - # Latest pip - None, - ] - code = dedent( - """\ - import sys - import traceback - try: - import salt.states.pip_state - salt.states.pip_state.InstallationError - except ImportError as exc: - traceback.print_exc(file=sys.stdout) - sys.stdout.flush() - sys.exit(1) - except AttributeError as exc: - traceback.print_exc(file=sys.stdout) - sys.stdout.flush() - sys.exit(2) - except Exception as exc: - traceback.print_exc(exc, file=sys.stdout) - sys.stdout.flush() - sys.exit(3) - sys.exit(0) - """ - ) - for requirement in list(pip_version_requirements): - try: - with VirtualEnv() as venv: - venv.install(*extra_requirements) - if requirement: - venv.install(f"pip{requirement}") - try: - subprocess.check_output([venv.venv_python, "-c", code]) - except subprocess.CalledProcessError as exc: - if exc.returncode == 1: - failures[requirement] = "Failed to import pip:\n{}".format( - exc.output - ) - elif exc.returncode == 2: - failures[ - requirement - ] = "Failed to import InstallationError from pip:\n{}".format( - exc.output - ) - else: - failures[requirement] = exc.output - except Exception as exc: # pylint: disable=broad-except - failures[requirement] = str(exc) - if failures: - errors = "" - for requirement, exception in failures.items(): - errors += "pip{}: {}\n\n".format(requirement or "", exception) - self.fail( - "Failed to get InstallationError exception under at least one pip" - " version:\n{}".format(errors) - ) diff --git a/tests/unit/utils/test_gitfs.py b/tests/unit/utils/test_gitfs.py index fa61f704908..7916a8eaecb 100644 --- a/tests/unit/utils/test_gitfs.py +++ b/tests/unit/utils/test_gitfs.py @@ -143,6 +143,7 @@ class TestGitBase(TestCase, AdaptedConfigurationTestCaseMixin): provider._master_lock.release() @pytest.mark.slow_test + @pytest.mark.timeout_unless_on_windows(120) def test_git_provider_mp_lock_timeout(self): """ Check that lock will time out if master lock is locked. @@ -157,6 +158,7 @@ class TestGitBase(TestCase, AdaptedConfigurationTestCaseMixin): provider._master_lock.release() @pytest.mark.slow_test + @pytest.mark.timeout_unless_on_windows(120) def test_git_provider_mp_clear_lock_timeout(self): """ Check that clear lock will time out if master lock is locked. diff --git a/tests/unit/utils/test_process.py b/tests/unit/utils/test_process.py index bc24e247638..2d7f7634d8b 100644 --- a/tests/unit/utils/test_process.py +++ b/tests/unit/utils/test_process.py @@ -26,6 +26,10 @@ except ImportError: log = logging.getLogger(__name__) +pytestmark = [ + pytest.mark.timeout_unless_on_windows(120), +] + def die(func): """ @@ -38,7 +42,7 @@ def die(func): name = func.__name__[5:] def _die(): - salt.utils.process.appendproctitle("test_{}".format(name)) + salt.utils.process.appendproctitle(f"test_{name}") attrname = "die_" + name setattr(self, attrname, _die) @@ -58,7 +62,7 @@ def incr(func): name = func.__name__[5:] def _incr(counter, num): - salt.utils.process.appendproctitle("test_{}".format(name)) + salt.utils.process.appendproctitle(f"test_{name}") for _ in range(0, num): counter.value += 1 @@ -80,7 +84,7 @@ def spin(func): name = func.__name__[5:] def _spin(): - salt.utils.process.appendproctitle("test_{}".format(name)) + salt.utils.process.appendproctitle(f"test_{name}") while True: time.sleep(1) diff --git a/tests/unit/utils/test_pyobjects.py b/tests/unit/utils/test_pyobjects.py index 6a7bd406237..981a3351cde 100644 --- a/tests/unit/utils/test_pyobjects.py +++ b/tests/unit/utils/test_pyobjects.py @@ -24,6 +24,10 @@ from salt.utils.pyobjects import ( from tests.support.runtests import RUNTIME_VARS from tests.support.unit import TestCase +pytestmark = [ + pytest.mark.timeout_unless_on_windows(240), +] + log = logging.getLogger(__name__)