Merge 3006.x into 3007.x

This commit is contained in:
Pedro Algarvio 2024-01-24 15:02:25 +00:00
commit 3448e40847
No known key found for this signature in database
GPG key ID: BB36BF6584A298FF
75 changed files with 1003 additions and 444 deletions

View file

@ -43,6 +43,7 @@ env:
PIP_INDEX_URL: https://pypi-proxy.saltstack.net/root/local/+simple/
PIP_EXTRA_INDEX_URL: https://pypi.org/simple
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
jobs:

View file

@ -19,6 +19,7 @@ env:
CACHE_SEED: SEED-4 # Bump the number to invalidate all caches
RELENV_DATA: "${{ github.workspace }}/.relenv"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
permissions:
contents: read # for dorny/paths-filter to fetch a list of changed files

View file

@ -25,6 +25,7 @@ env:
CACHE_SEED: SEED-4 # Bump the number to invalidate all caches
RELENV_DATA: "${{ github.workspace }}/.relenv"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
permissions:
contents: read # for dorny/paths-filter to fetch a list of changed files

View file

@ -24,6 +24,7 @@ env:
CACHE_SEED: SEED-4 # Bump the number to invalidate all caches
RELENV_DATA: "${{ github.workspace }}/.relenv"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
permissions:
contents: write # To be able to publish the release

View file

@ -15,6 +15,7 @@ env:
CACHE_SEED: SEED-4 # Bump the number to invalidate all caches
RELENV_DATA: "${{ github.workspace }}/.relenv"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
permissions:
contents: read # for dorny/paths-filter to fetch a list of changed files

View file

@ -40,6 +40,7 @@ env:
CACHE_SEED: SEED-4 # Bump the number to invalidate all caches
RELENV_DATA: "${{ github.workspace }}/.relenv"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
permissions:
contents: read # for dorny/paths-filter to fetch a list of changed files

View file

@ -43,6 +43,7 @@ env:
PIP_INDEX_URL: https://pypi-proxy.saltstack.net/root/local/+simple/
PIP_EXTRA_INDEX_URL: https://pypi.org/simple
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
jobs:

View file

@ -37,6 +37,7 @@ env:
CACHE_SEED: SEED-4 # Bump the number to invalidate all caches
RELENV_DATA: "${{ github.workspace }}/.relenv"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
<%- endblock env %>

View file

@ -51,6 +51,7 @@ env:
PIP_INDEX_URL: https://pypi-proxy.saltstack.net/root/local/+simple/
PIP_EXTRA_INDEX_URL: https://pypi.org/simple
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
jobs:

View file

@ -73,6 +73,7 @@ env:
PIP_INDEX_URL: https://pypi-proxy.saltstack.net/root/local/+simple/
PIP_EXTRA_INDEX_URL: https://pypi.org/simple
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
jobs:

View file

@ -66,6 +66,7 @@ env:
PIP_INDEX_URL: "https://pypi-proxy.saltstack.net/root/local/+simple/"
PIP_EXTRA_INDEX_URL: "https://pypi.org/simple"
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
jobs:

View file

@ -73,6 +73,7 @@ env:
PIP_INDEX_URL: https://pypi-proxy.saltstack.net/root/local/+simple/
PIP_EXTRA_INDEX_URL: https://pypi.org/simple
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
jobs:

View file

@ -51,6 +51,7 @@ env:
PIP_INDEX_URL: https://pypi-proxy.saltstack.net/root/local/+simple/
PIP_EXTRA_INDEX_URL: https://pypi.org/simple
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
jobs:

View file

@ -68,6 +68,7 @@ env:
PIP_INDEX_URL: https://pypi-proxy.saltstack.net/root/local/+simple/
PIP_EXTRA_INDEX_URL: https://pypi.org/simple
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
jobs:

View file

@ -61,6 +61,7 @@ env:
PIP_INDEX_URL: https://pypi-proxy.saltstack.net/root/local/+simple/
PIP_EXTRA_INDEX_URL: https://pypi.org/simple
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
jobs:

View file

@ -68,6 +68,7 @@ env:
PIP_INDEX_URL: https://pypi-proxy.saltstack.net/root/local/+simple/
PIP_EXTRA_INDEX_URL: https://pypi.org/simple
PIP_DISABLE_PIP_VERSION_CHECK: "1"
RAISE_DEPRECATIONS_RUNTIME_ERRORS: "1"
jobs:

1
changelog/65777.fixed.md Normal file
View file

@ -0,0 +1 @@
Fixes an issue when reading/modifying ini files that contain unicode characters

1
changelog/65824.fixed.md Normal file
View file

@ -0,0 +1 @@
added https proxy to the list of proxies so that requests knows what to do with https based proxies

View file

@ -0,0 +1 @@
Salt no longer time bombs user installations on code using `salt.utils.versions.warn_until_date`

View file

@ -11,3 +11,4 @@ pytest-custom-exit-code >= 0.3
flaky
more-itertools
pyfakefs
trustme

View file

@ -105,8 +105,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -127,7 +128,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.10/darwin.txt
# aiohttp
@ -153,6 +154,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.10/darwin.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -505,6 +507,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -104,8 +104,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -126,7 +127,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.10/freebsd.txt
# aiohttp
@ -152,6 +153,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.10/freebsd.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -510,6 +512,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -118,8 +118,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -142,7 +143,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.10/linux.txt
# aiohttp
@ -176,6 +177,7 @@ idna==3.4
# etcd3-py
# httpx
# requests
# trustme
# yarl
immutables==0.15
# via
@ -572,6 +574,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -102,7 +102,8 @@ cryptography==41.0.7
# pyopenssl
# pyspnego
# requests-ntlm
distlib==0.3.7
# trustme
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -125,7 +126,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.10/windows.txt
# aiohttp
@ -145,6 +146,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.10/windows.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -456,6 +458,8 @@ tornado==6.3.3
# via
# -c requirements/static/ci/../pkg/py3.10/windows.txt
# -r requirements/base.txt
trustme==1.1.0
# via -r requirements/pytest.txt
types-pyyaml==6.0.1
# via responses
typing-extensions==4.8.0

View file

@ -105,8 +105,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -125,7 +126,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.11/darwin.txt
# aiohttp
@ -151,6 +152,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.11/darwin.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -505,6 +507,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -104,8 +104,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -124,7 +125,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.11/freebsd.txt
# aiohttp
@ -150,6 +151,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.11/freebsd.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -511,6 +513,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -118,8 +118,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -138,7 +139,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.11/linux.txt
# aiohttp
@ -172,6 +173,7 @@ idna==3.4
# etcd3-py
# httpx
# requests
# trustme
# yarl
immutables==0.15
# via
@ -571,6 +573,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -102,7 +102,8 @@ cryptography==41.0.7
# pyopenssl
# pyspnego
# requests-ntlm
distlib==0.3.7
# trustme
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -123,7 +124,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.11/windows.txt
# aiohttp
@ -143,6 +144,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.11/windows.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -456,6 +458,8 @@ tornado==6.3.3
# via
# -c requirements/static/ci/../pkg/py3.11/windows.txt
# -r requirements/base.txt
trustme==1.1.0
# via -r requirements/pytest.txt
types-pyyaml==6.0.12.12
# via responses
typing-extensions==4.8.0

View file

@ -142,8 +142,9 @@ cryptography==41.0.7
# pyspnego
# requests-ntlm
# smbprotocol
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via
# -c requirements/static/ci/py3.12/linux.txt
# virtualenv
@ -174,7 +175,7 @@ flaky==3.7.0
# via
# -c requirements/static/ci/py3.12/linux.txt
# -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.12/linux.txt
# -c requirements/static/ci/py3.12/linux.txt
@ -211,6 +212,7 @@ idna==3.4
# -c requirements/static/ci/py3.12/linux.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -731,6 +733,10 @@ transitions==0.9.0
# via
# -c requirements/static/ci/py3.12/linux.txt
# junos-eznc
trustme==1.1.0
# via
# -c requirements/static/ci/py3.12/linux.txt
# -r requirements/pytest.txt
ttp-templates==0.3.5
# via
# -c requirements/static/ci/py3.12/linux.txt

View file

@ -105,8 +105,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -151,6 +152,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.12/darwin.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -505,6 +507,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -64,7 +64,7 @@ distro==1.8.0
# -r requirements/base.txt
docutils==0.20.1
# via sphinx
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/py3.12/linux.txt
# aiohttp

View file

@ -104,8 +104,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -124,7 +125,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.12/freebsd.txt
# aiohttp
@ -150,6 +151,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.12/freebsd.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -511,6 +513,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -158,7 +158,7 @@ cryptography==41.0.7
# paramiko
# pyopenssl
# vcert
distlib==0.3.7
distlib==0.3.8
# via
# -c requirements/static/ci/py3.12/linux.txt
# virtualenv
@ -180,7 +180,7 @@ filelock==3.13.1
# via
# -c requirements/static/ci/py3.12/linux.txt
# virtualenv
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.12/linux.txt
# -c requirements/static/ci/py3.12/linux.txt

View file

@ -118,8 +118,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -138,7 +139,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.12/linux.txt
# aiohttp
@ -172,6 +173,7 @@ idna==3.4
# etcd3-py
# httpx
# requests
# trustme
# yarl
immutables==0.15
# via
@ -571,6 +573,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -102,7 +102,8 @@ cryptography==41.0.7
# pyopenssl
# pyspnego
# requests-ntlm
distlib==0.3.7
# trustme
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -123,7 +124,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.12/windows.txt
# aiohttp
@ -143,6 +144,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.12/windows.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -456,6 +458,8 @@ tornado==6.3.3
# via
# -c requirements/static/ci/../pkg/py3.12/windows.txt
# -r requirements/base.txt
trustme==1.1.0
# via -r requirements/pytest.txt
types-pyyaml==6.0.12.12
# via responses
typing-extensions==4.8.0

View file

@ -104,8 +104,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -126,7 +127,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.8/freebsd.txt
# aiohttp
@ -152,6 +153,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.8/freebsd.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -514,6 +516,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -113,8 +113,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -137,7 +138,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.8/linux.txt
# aiohttp
@ -171,6 +172,7 @@ idna==3.4
# etcd3-py
# httpx
# requests
# trustme
# yarl
immutables==0.15
# via
@ -566,6 +568,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -102,7 +102,8 @@ cryptography==41.0.7
# pyopenssl
# pyspnego
# requests-ntlm
distlib==0.3.7
# trustme
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -125,7 +126,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.8/windows.txt
# aiohttp
@ -145,6 +146,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.8/windows.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -461,6 +463,8 @@ tornado==6.3.3
# via
# -c requirements/static/ci/../pkg/py3.8/windows.txt
# -r requirements/base.txt
trustme==1.1.0
# via -r requirements/pytest.txt
types-pyyaml==6.0.1
# via responses
typing-extensions==4.8.0

View file

@ -105,8 +105,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -127,7 +128,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.9/darwin.txt
# aiohttp
@ -153,6 +154,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.9/darwin.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -505,6 +507,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -104,8 +104,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -126,7 +127,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.9/freebsd.txt
# aiohttp
@ -152,6 +153,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.9/freebsd.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -510,6 +512,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -113,8 +113,9 @@ cryptography==41.0.7
# moto
# paramiko
# pyopenssl
# trustme
# vcert
distlib==0.3.7
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -137,7 +138,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.9/linux.txt
# aiohttp
@ -171,6 +172,7 @@ idna==3.4
# etcd3-py
# httpx
# requests
# trustme
# yarl
immutables==0.15
# via
@ -562,6 +564,8 @@ tornado==6.3.3
# -r requirements/base.txt
transitions==0.9.0
# via junos-eznc
trustme==1.1.0
# via -r requirements/pytest.txt
ttp-templates==0.3.5
# via napalm
ttp==0.9.5

View file

@ -102,7 +102,8 @@ cryptography==41.0.7
# pyopenssl
# pyspnego
# requests-ntlm
distlib==0.3.7
# trustme
distlib==0.3.8
# via virtualenv
distro==1.8.0
# via
@ -125,7 +126,7 @@ filelock==3.13.1
# via virtualenv
flaky==3.7.0
# via -r requirements/pytest.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# -c requirements/static/ci/../pkg/py3.9/windows.txt
# aiohttp
@ -145,6 +146,7 @@ idna==3.4
# -c requirements/static/ci/../pkg/py3.9/windows.txt
# etcd3-py
# requests
# trustme
# yarl
immutables==0.15
# via
@ -457,6 +459,8 @@ tornado==6.3.3
# via
# -c requirements/static/ci/../pkg/py3.9/windows.txt
# -r requirements/base.txt
trustme==1.1.0
# via -r requirements/pytest.txt
types-pyyaml==6.0.1
# via responses
typing-extensions==4.8.0

View file

@ -32,7 +32,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -32,7 +32,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -32,7 +32,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -36,7 +36,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -32,7 +32,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -32,7 +32,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -32,7 +32,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -36,7 +36,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -32,7 +32,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -32,7 +32,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -36,7 +36,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -32,7 +32,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -32,7 +32,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -36,7 +36,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -32,7 +32,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -32,7 +32,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -32,7 +32,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -36,7 +36,7 @@ cryptography==41.0.7
# pyopenssl
distro==1.8.0
# via -r requirements/base.txt
frozenlist==1.4.0
frozenlist==1.4.1
# via
# aiohttp
# aiosignal

View file

@ -110,52 +110,15 @@ warnings.filterwarnings(
def __define_global_system_encoding_variable__():
# This is the most trustworthy source of the system encoding, though, if
# salt is being imported after being daemonized, this information is lost
# and reset to None
encoding = None
if not sys.platform.startswith("win") and sys.stdin is not None:
# On linux we can rely on sys.stdin for the encoding since it
# most commonly matches the filesystem encoding. This however
# does not apply to windows
encoding = sys.stdin.encoding
if not encoding:
# If the system is properly configured this should return a valid
# encoding. MS Windows has problems with this and reports the wrong
# encoding
try:
encoding = locale.getencoding()
except AttributeError:
# Python < 3.11
encoding = locale.getpreferredencoding(do_setlocale=True)
if not encoding:
# This is most likely ascii which is not the best but we were
# unable to find a better encoding. If this fails, we fall all
# the way back to ascii
encoding = sys.getdefaultencoding()
if not encoding:
if sys.platform.startswith("darwin"):
# Mac OS X uses UTF-8
encoding = "utf-8"
elif sys.platform.startswith("win"):
# Windows uses a configurable encoding; on Windows, Python uses the name “mbcs”
# to refer to whatever the currently configured encoding is.
encoding = "mbcs"
else:
# On linux default to ascii as a last resort
encoding = "ascii"
import builtins
import sys
# Define the detected encoding as a built-in variable for ease of use
setattr(builtins, "__salt_system_encoding__", encoding)
setattr(builtins, "__salt_system_encoding__", sys.getdefaultencoding())
# This is now garbage collectable
del builtins
del encoding
del sys
__define_global_system_encoding_variable__()

View file

@ -75,6 +75,16 @@ structure::
More info here:
https://docs.aws.amazon.com/cli/latest/topic/s3-config.html
.. note:: This fileserver back-end will by default sync all buckets on every
fileserver update.
If you want files to be only populated in the cache when requested, you can
disable this in the master config:
.. code-block:: yaml
s3.s3_sync_on_update: False
"""
@ -94,9 +104,6 @@ import salt.utils.versions
log = logging.getLogger(__name__)
S3_CACHE_EXPIRE = 30 # cache for 30 seconds
S3_SYNC_ON_UPDATE = True # sync cache on update rather than jit
def envs():
"""
@ -116,7 +123,8 @@ def update():
metadata = _init()
if S3_SYNC_ON_UPDATE:
# sync cache on update rather than jit
if __opts__.get("s3.s3_sync_on_update", True):
# sync the buckets to the local cache
log.info("Syncing local cache from S3...")
for saltenv, env_meta in metadata.items():
@ -343,7 +351,7 @@ def _init():
specified and cache the data to disk.
"""
cache_file = _get_buckets_cache_filename()
exp = time.time() - S3_CACHE_EXPIRE
exp = time.time() - __opts__.get("s3.s3_cache_expire", 30)
# check mtime of the buckets files cache
metadata = None

View file

@ -37,26 +37,37 @@ COM_REGX = re.compile(r"^\s*(#|;)\s*(.*)")
INDENTED_REGX = re.compile(r"(\s+)(.*)")
def set_option(file_name, sections=None, separator="="):
def set_option(file_name, sections=None, separator="=", encoding=None):
"""
Edit an ini file, replacing one or more sections. Returns a dictionary
containing the changes made.
file_name
path of ini_file
Args:
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
file_name (str):
The full path to the ini file.
separator : =
A character used to separate keys and values. Standard ini files use
the "=" character.
sections (dict):
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 ``None``.
.. versionadded:: 2016.11.0
separator (str):
The character used to separate keys and values. Standard ini files
use the "=" character. The default is ``=``.
.. versionadded:: 2016.11.0
encoding (str):
A string value representing encoding of the target ini file. If
``None`` is passed, it uses the system default which is likely
``utf-8``. Default is ``None``
.. versionadded:: 3006.6
Returns:
dict: A dictionary representing the changes made to the ini file
API Example:
@ -65,8 +76,7 @@ def set_option(file_name, sections=None, separator="="):
import salt.client
with salt.client.get_local_client() as sc:
sc.cmd(
'target', 'ini.set_option',
['path_to_ini_file', '{"section_to_change": {"key": "value"}}']
'target', 'ini.set_option', ['path_to_ini_file', '{"section_to_change": {"key": "value"}}']
)
CLI Example:
@ -74,36 +84,67 @@ def set_option(file_name, sections=None, separator="="):
.. code-block:: bash
salt '*' ini.set_option /path/to/ini '{section_foo: {key: value}}'
"""
sections = sections or {}
changes = {}
inifile = _Ini.get_ini_file(file_name, separator=separator)
inifile = _Ini.get_ini_file(file_name, separator=separator, encoding=encoding)
changes = inifile.update(sections)
inifile.flush()
return changes
def get_option(file_name, section, option, separator="="):
def get_option(file_name, section, option, separator="=", encoding=None):
"""
Get value of a key from a section in an ini file. Returns ``None`` if
no matching key was found.
Args:
file_name (str):
The full path to the ini file.
section (str):
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.
option (str):
A string value representing the option to search for.
separator (str):
The character used to separate keys and values. Standard ini files
use the "=" character. The default is ``=``.
.. versionadded:: 2016.11.0
encoding (str):
A string value representing encoding of the target ini file. If
``None`` is passed, it uses the system default which is likely
``utf-8``. Default is ``None``
.. versionadded:: 3006.6
Returns:
str: The value as defined in the ini file, or ``None`` if empty or not
found
API Example:
.. code-block:: python
import salt.client
with salt.client.get_local_client() as sc:
sc.cmd('target', 'ini.get_option',
[path_to_ini_file, section_name, option])
sc.cmd('target', 'ini.get_option', [path_to_ini_file, section_name, option])
CLI Example:
.. code-block:: bash
salt '*' ini.get_option /path/to/ini section_name option_name
"""
inifile = _Ini.get_ini_file(file_name, separator=separator)
inifile = _Ini.get_ini_file(file_name, separator=separator, encoding=encoding)
if section:
try:
return inifile.get(section, {}).get(option, None)
@ -113,27 +154,57 @@ def get_option(file_name, section, option, separator="="):
return inifile.get(option, None)
def remove_option(file_name, section, option, separator="="):
def remove_option(file_name, section, option, separator="=", encoding=None):
"""
Remove a key/value pair from a section in an ini file. Returns the value of
the removed key, or ``None`` if nothing was removed.
Args:
file_name (str):
The full path to the ini file.
section (str):
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.
option (str):
A string value representing the option to search for.
separator (str):
The character used to separate keys and values. Standard ini files
use the "=" character. The default is ``=``.
.. versionadded:: 2016.11.0
encoding (str):
A string value representing encoding of the target ini file. If
``None`` is passed, it uses the system default which is likely
``utf-8``. Default is ``None``
.. versionadded:: 3006.6
Returns:
str: A string value representing the option that was removed or ``None``
if nothing was removed
API Example:
.. code-block:: python
import salt
sc = salt.client.get_local_client()
sc.cmd('target', 'ini.remove_option',
[path_to_ini_file, section_name, option])
sc.cmd('target', 'ini.remove_option', [path_to_ini_file, section_name, option])
CLI Example:
.. code-block:: bash
salt '*' ini.remove_option /path/to/ini section_name option_name
"""
inifile = _Ini.get_ini_file(file_name, separator=separator)
inifile = _Ini.get_ini_file(file_name, separator=separator, encoding=encoding)
if isinstance(inifile.get(section), (dict, OrderedDict)):
value = inifile.get(section, {}).pop(option, None)
else:
@ -142,27 +213,54 @@ def remove_option(file_name, section, option, separator="="):
return value
def get_section(file_name, section, separator="="):
def get_section(file_name, section, separator="=", encoding=None):
"""
Retrieve a section from an ini file. Returns the section as dictionary. If
Retrieve a section from an ini file. Returns the section as a dictionary. If
the section is not found, an empty dictionary is returned.
Args:
file_name (str):
The full path to the ini file.
section (str):
A string value representing name of the section to search for.
separator (str):
The character used to separate keys and values. Standard ini files
use the "=" character. The default is ``=``.
.. versionadded:: 2016.11.0
encoding (str):
A string value representing encoding of the target ini file. If
``None`` is passed, it uses the system default which is likely
``utf-8``. Default is ``None``
.. versionadded:: 3006.6
Returns:
dict: A dictionary containing 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
API Example:
.. code-block:: python
import salt.client
with salt.client.get_local_client() as sc:
sc.cmd('target', 'ini.get_section',
[path_to_ini_file, section_name])
sc.cmd('target', 'ini.get_section', [path_to_ini_file, section_name])
CLI Example:
.. code-block:: bash
salt '*' ini.get_section /path/to/ini section_name
"""
inifile = _Ini.get_ini_file(file_name, separator=separator)
inifile = _Ini.get_ini_file(file_name, separator=separator, encoding=encoding)
ret = {}
for key, value in inifile.get(section, {}).items():
if key[0] != "#":
@ -170,10 +268,35 @@ def get_section(file_name, section, separator="="):
return ret
def remove_section(file_name, section, separator="="):
def remove_section(file_name, section, separator="=", encoding=None):
"""
Remove a section in an ini file. Returns the removed section as dictionary,
or ``None`` if nothing was removed.
Remove a section in an ini file. Returns the removed section as a
dictionary, or ``None`` if nothing is removed.
Args:
file_name (str):
The full path to the ini file.
section (str):
A string value representing the name of the section search for.
separator (str):
The character used to separate keys and values. Standard ini files
use the "=" character. The default is ``=``.
.. versionadded:: 2016.11.0
encoding (str):
A string value representing encoding of the target ini file. If
``None`` is passed, it uses the system default which is likely
``utf-8``. Default is ``None``
.. versionadded:: 3006.6
Returns:
dict: A dictionary containing the names and values of all items in the
section that was removed or ``None`` if nothing was removed
API Example:
@ -181,16 +304,17 @@ def remove_section(file_name, section, separator="="):
import salt.client
with salt.client.get_local_client() as sc:
sc.cmd('target', 'ini.remove_section',
[path_to_ini_file, section_name])
sc.cmd('target', 'ini.remove_section', [path_to_ini_file, section_name])
CLI Example:
.. code-block:: bash
salt '*' ini.remove_section /path/to/ini section_name
"""
inifile = _Ini.get_ini_file(file_name, separator=separator)
inifile = _Ini.get_ini_file(file_name, separator=separator, encoding=encoding)
if section in inifile:
section = inifile.pop(section)
inifile.flush()
@ -201,24 +325,46 @@ def remove_section(file_name, section, separator="="):
return ret
def get_ini(file_name, separator="="):
def get_ini(file_name, separator="=", encoding=None):
"""
Retrieve whole structure from an ini file and return it as dictionary.
Retrieve the whole structure from an ini file and return it as a dictionary.
Args:
file_name (str):
The full path to the ini file.
separator (str):
The character used to separate keys and values. Standard ini files
use the "=" character. The default is ``=``.
.. versionadded:: 2016.11.0
encoding (str):
A string value representing encoding of the target ini file. If
``None`` is passed, it uses the system default which is likely
``utf-8``. Default is ``None``
.. versionadded:: 3006.6
Returns:
dict: A dictionary containing the sections along with the values and
names contained in each section
API Example:
.. code-block:: python
import salt.client
with salt.client.giet_local_client() as sc:
sc.cmd('target', 'ini.get_ini',
[path_to_ini_file])
with salt.client.get_local_client() as sc:
sc.cmd('target', 'ini.get_ini', [path_to_ini_file])
CLI Example:
.. code-block:: bash
salt '*' ini.get_ini /path/to/ini
"""
def ini_odict2dict(odict):
@ -227,6 +373,7 @@ def get_ini(file_name, separator="="):
:param odict: OrderedDict
:return: regular dict
"""
ret = {}
for key, val in odict.items():
if key[0] != "#":
@ -236,7 +383,7 @@ def get_ini(file_name, separator="="):
ret.update({key: val})
return ret
inifile = _Ini.get_ini_file(file_name, separator=separator)
inifile = _Ini.get_ini_file(file_name, separator=separator, encoding=encoding)
return ini_odict2dict(inifile)
@ -409,14 +556,29 @@ class _Section(OrderedDict):
class _Ini(_Section):
def __init__(
self, name, inicontents="", separator="=", commenter="#", encoding=None
):
super().__init__(
self, inicontents=inicontents, separator=separator, commenter=commenter
)
self.name = name
if encoding is None:
encoding = __salt_system_encoding__
self.encoding = encoding
def refresh(self, inicontents=None):
if inicontents is None:
if not os.path.exists(self.name):
log.trace("File %s does not exist and will be created", self.name)
return
try:
with salt.utils.files.fopen(self.name) as rfh:
inicontents = salt.utils.stringutils.to_unicode(rfh.read())
# We need to set decode on open and not try to do it later with
# stringutils
with salt.utils.files.fopen(
self.name, "r", encoding=self.encoding
) as rfh:
inicontents = rfh.read()
inicontents = os.linesep.join(inicontents.splitlines())
except OSError as exc:
if __opts__["test"] is False:
@ -443,22 +605,27 @@ class _Ini(_Section):
def flush(self):
try:
with salt.utils.files.fopen(self.name, "wb") as outfile:
# We need to encode in the fopen command instead of using
# data.encode in the writelines command. Using data.encode will
# cause a BoM to be placed on every line of the file
with salt.utils.files.fopen(
self.name, "w", encoding=self.encoding
) as outfile:
ini_gen = self.gen_ini()
next(ini_gen)
ini_gen_list = list(ini_gen)
# Avoid writing an initial line separator.
if ini_gen_list:
ini_gen_list[0] = ini_gen_list[0].lstrip(os.linesep)
outfile.writelines(salt.utils.data.encode(ini_gen_list))
outfile.writelines(ini_gen_list)
except OSError as exc:
raise CommandExecutionError(
"Unable to write file '{}'. Exception: {}".format(self.name, exc)
)
@staticmethod
def get_ini_file(file_name, separator="="):
inifile = _Ini(file_name, separator=separator)
def get_ini_file(file_name, separator="=", encoding=None):
inifile = _Ini(file_name, separator=separator, encoding=encoding)
inifile.refresh()
return inifile

View file

@ -12,7 +12,7 @@ Hives
This is the top level of the registry. They all begin with HKEY.
- HKEY_CLASSES_ROOT (HKCR)
- HKEY_CURRENT_USER(HKCU)
- HKEY_CURRENT_USER (HKCU)
- HKEY_LOCAL MACHINE (HKLM)
- HKEY_USER (HKU)
- HKEY_CURRENT_CONFIG
@ -139,7 +139,7 @@ def present(
A string value representing the full path of the key to include the
HIVE, Key, and all Subkeys. For example:
``HKEY_LOCAL_MACHINE\\SOFTWARE\\Salt``
``HKEY_LOCAL_MACHINE\SOFTWARE\Salt``
Valid hive values include:
@ -298,24 +298,24 @@ def present(
Example:
The following example will set the ``(Default)`` value for the
``SOFTWARE\\Salt`` key in the ``HKEY_CURRENT_USER`` hive to
``SOFTWARE\Salt`` key in the ``HKEY_CURRENT_USER`` hive to
``2016.3.1``:
.. code-block:: yaml
HKEY_CURRENT_USER\\SOFTWARE\\Salt:
HKEY_CURRENT_USER\SOFTWARE\Salt:
reg.present:
- vdata: 2016.3.1
Example:
The following example will set the value for the ``version`` entry under
the ``SOFTWARE\\Salt`` key in the ``HKEY_CURRENT_USER`` hive to
the ``SOFTWARE\Salt`` key in the ``HKEY_CURRENT_USER`` hive to
``2016.3.1``. The value will be reflected in ``Wow6432Node``:
.. code-block:: yaml
HKEY_CURRENT_USER\\SOFTWARE\\Salt:
HKEY_CURRENT_USER\SOFTWARE\Salt:
reg.present:
- vname: version
- vdata: 2016.3.1
@ -323,7 +323,7 @@ def present(
In the above example the path is interpreted as follows:
- ``HKEY_CURRENT_USER`` is the hive
- ``SOFTWARE\\Salt`` is the key
- ``SOFTWARE\Salt`` is the key
- ``vname`` is the value name ('version') that will be created under the key
- ``vdata`` is the data that will be assigned to 'version'
@ -423,7 +423,7 @@ def present(
)
add_change = {
"Key": r"{}\{}".format(hive, key),
"Key": rf"{hive}\{key}",
"Entry": "{}".format(
salt.utils.stringutils.to_unicode(vname, "utf-8") if vname else "(Default)"
),
@ -451,10 +451,10 @@ def present(
if not ret["result"]:
ret["changes"] = {}
ret["comment"] = r"Failed to add {} to {}\{}".format(vname, hive, key)
ret["comment"] = rf"Failed to add {vname} to {hive}\{key}"
else:
ret["changes"] = {"reg": {"Added": add_change}}
ret["comment"] = r"Added {} to {}\{}".format(vname, hive, key)
ret["comment"] = rf"Added {vname} to {hive}\{key}"
if ret["result"]:
ret = __utils__["dacl.check_perms"](
@ -480,7 +480,7 @@ def absent(name, vname=None, use_32bit_registry=False):
A string value representing the full path of the key to include the
HIVE, Key, and all Subkeys. For example:
``HKEY_LOCAL_MACHINE\\SOFTWARE\\Salt``
``HKEY_LOCAL_MACHINE\SOFTWARE\Salt``
Valid hive values include:
@ -504,7 +504,7 @@ def absent(name, vname=None, use_32bit_registry=False):
.. code-block:: yaml
'HKEY_CURRENT_USER\\SOFTWARE\\Salt':
'HKEY_CURRENT_USER\SOFTWARE\Salt':
reg.absent
- vname: version
@ -521,11 +521,11 @@ def absent(name, vname=None, use_32bit_registry=False):
hive=hive, key=key, vname=vname, use_32bit_registry=use_32bit_registry
)
if not reg_check["success"] or reg_check["vdata"] == "(value not set)":
ret["comment"] = "{} is already absent".format(name)
ret["comment"] = f"{name} is already absent"
return ret
remove_change = {
"Key": r"{}\{}".format(hive, key),
"Key": rf"{hive}\{key}",
"Entry": "{}".format(vname if vname else "(Default)"),
}
@ -541,10 +541,10 @@ def absent(name, vname=None, use_32bit_registry=False):
)
if not ret["result"]:
ret["changes"] = {}
ret["comment"] = r"Failed to remove {} from {}".format(key, hive)
ret["comment"] = rf"Failed to remove {key} from {hive}"
else:
ret["changes"] = {"reg": {"Removed": remove_change}}
ret["comment"] = r"Removed {} from {}".format(key, hive)
ret["comment"] = rf"Removed {key} from {hive}"
return ret
@ -598,10 +598,10 @@ def key_absent(name, use_32bit_registry=False):
if not __utils__["reg.read_value"](
hive=hive, key=key, use_32bit_registry=use_32bit_registry
)["success"]:
ret["comment"] = "{} is already absent".format(name)
ret["comment"] = f"{name} is already absent"
return ret
ret["changes"] = {"reg": {"Removed": {"Key": r"{}\{}".format(hive, key)}}}
ret["changes"] = {"reg": {"Removed": {"Key": rf"{hive}\{key}"}}}
# Check for test option
if __opts__["test"]:
@ -617,6 +617,6 @@ def key_absent(name, use_32bit_registry=False):
)["success"]:
ret["result"] = False
ret["changes"] = {}
ret["comment"] = "Failed to remove registry key {}".format(name)
ret["comment"] = f"Failed to remove registry key {name}"
return ret

View file

@ -169,7 +169,7 @@ def _remove_circular_refs(ob, _seen=None):
This has been taken from author Martijn Pieters
https://stackoverflow.com/questions/44777369/
remove-circular-references-in-dicts-lists-tuples/44777477#44777477
:param ob: dict, list, typle, set, and frozenset
:param ob: dict, list, tuple, set, and frozenset
Standard python object
:param object _seen:
Object that has circular reference

View file

@ -363,7 +363,10 @@ def query(
sess_cookies = sess.cookies
sess.verify = verify_ssl
if http_proxy_url is not None:
sess.proxies = {"http": http_proxy_url}
sess.proxies = {
"http": http_proxy_url,
"https": http_proxy_url,
}
elif backend == "urllib2":
sess_cookies = None
else:

View file

@ -6,8 +6,6 @@
which works under python 3 because on python 3 you can no longer compare
strings against integers.
"""
import datetime
import inspect
import logging
@ -172,7 +170,7 @@ def warn_until(
if _version_ >= version:
caller = inspect.getframeinfo(sys._getframe(stacklevel - 1))
raise RuntimeError(
deprecated_message = (
"The warning triggered on filename '{filename}', line number "
"{lineno}, is supposed to be shown until version "
"{until_version} is released. Current version is now "
@ -181,8 +179,15 @@ def warn_until(
lineno=caller.lineno,
until_version=version.formatted_version,
salt_version=_version_.formatted_version,
),
)
)
if os.environ.get("RAISE_DEPRECATIONS_RUNTIME_ERRORS", "0") == "1":
# We don't raise RuntimeError by default since that can break
# users systems. We do however want to raise them in a CI context.
raise RuntimeError(deprecated_message)
# Otherwise, print the deprecated message to STDERR
sys.stderr.write(f"\n{deprecated_message}\n")
sys.stderr.flush()
if _dont_call_warnings is False and os.environ.get("PYTHONWARNINGS") != "ignore":
warnings.warn(
@ -240,7 +245,7 @@ def warn_until_date(
today = _current_date or datetime.datetime.utcnow().date()
if today >= date:
caller = inspect.getframeinfo(sys._getframe(stacklevel - 1))
raise RuntimeError(
deprecated_message = (
"{message} This warning(now exception) triggered on "
"filename '{filename}', line number {lineno}, is "
"supposed to be shown until {date}. Today is {today}. "
@ -252,6 +257,13 @@ def warn_until_date(
today=today.isoformat(),
),
)
if os.environ.get("RAISE_DEPRECATIONS_RUNTIME_ERRORS", "0") == "1":
# We don't raise RuntimeError by default since that can break
# users systems. We do however want to raise them in a CI context.
raise RuntimeError(deprecated_message)
# Otherwise, print the deprecated message to STDERR
sys.stderr.write(f"\n{deprecated_message}\n")
sys.stderr.flush()
if _dont_call_warnings is False and os.environ.get("PYTHONWARNINGS") != "ignore":
warnings.warn(

View file

@ -23,9 +23,8 @@ Values/Entries are name/data pairs. There can be many values in a key. The
(Default) value corresponds to the Key itself, the rest are their own name/value
pairs.
:depends: - PyWin32
:depends: PyWin32
"""
# When production windows installer is using Python 3, Python 2 code can be removed
import logging
@ -73,7 +72,7 @@ def __virtual__():
def _to_mbcs(vdata):
"""
Converts unicode to to current users character encoding. Use this for values
Converts unicode to current users character encoding. Use this for values
returned by reg functions
"""
return salt.utils.stringutils.to_unicode(vdata, "mbcs")
@ -186,7 +185,7 @@ def key_exists(hive, key, use_32bit_registry=False):
try:
hkey = registry.hkeys[local_hive]
except KeyError:
raise CommandExecutionError("Invalid Hive: {}".format(local_hive))
raise CommandExecutionError(f"Invalid Hive: {local_hive}")
access_mask = registry.registry_32[use_32bit_registry]
handle = None
@ -241,7 +240,7 @@ def value_exists(hive, key, vname, use_32bit_registry=False):
try:
hkey = registry.hkeys[local_hive]
except KeyError:
raise CommandExecutionError("Invalid Hive: {}".format(local_hive))
raise CommandExecutionError(f"Invalid Hive: {local_hive}")
access_mask = registry.registry_32[use_32bit_registry]
try:
@ -340,7 +339,7 @@ def list_keys(hive, key=None, use_32bit_registry=False):
try:
hkey = registry.hkeys[local_hive]
except KeyError:
raise CommandExecutionError("Invalid Hive: {}".format(local_hive))
raise CommandExecutionError(f"Invalid Hive: {local_hive}")
access_mask = registry.registry_32[use_32bit_registry]
subkeys = []
@ -355,10 +354,10 @@ def list_keys(hive, key=None, use_32bit_registry=False):
except win32api.error as exc:
if exc.winerror == 2:
log.debug(r"Cannot find key: %s\%s", hive, key, exc_info=True)
return False, r"Cannot find key: {}\{}".format(hive, key)
return False, rf"Cannot find key: {hive}\{key}"
if exc.winerror == 5:
log.debug(r"Access is denied: %s\%s", hive, key, exc_info=True)
return False, r"Access is denied: {}\{}".format(hive, key)
return False, rf"Access is denied: {hive}\{key}"
raise
finally:
@ -412,7 +411,7 @@ def list_values(hive, key=None, use_32bit_registry=False):
try:
hkey = registry.hkeys[local_hive]
except KeyError:
raise CommandExecutionError("Invalid Hive: {}".format(local_hive))
raise CommandExecutionError(f"Invalid Hive: {local_hive}")
access_mask = registry.registry_32[use_32bit_registry]
handle = None
values = list()
@ -445,10 +444,10 @@ def list_values(hive, key=None, use_32bit_registry=False):
except win32api.error as exc:
if exc.winerror == 2:
log.debug(r"Cannot find key: %s\%s", hive, key)
return False, r"Cannot find key: {}\{}".format(hive, key)
return False, rf"Cannot find key: {hive}\{key}"
elif exc.winerror == 5:
log.debug(r"Access is denied: %s\%s", hive, key)
return False, r"Access is denied: {}\{}".format(hive, key)
return False, rf"Access is denied: {hive}\{key}"
raise
finally:
@ -538,7 +537,7 @@ def read_value(hive, key, vname=None, use_32bit_registry=False):
try:
hkey = registry.hkeys[local_hive]
except KeyError:
raise CommandExecutionError("Invalid Hive: {}".format(local_hive))
raise CommandExecutionError(f"Invalid Hive: {local_hive}")
access_mask = registry.registry_32[use_32bit_registry]
handle = None
@ -574,13 +573,13 @@ def read_value(hive, key, vname=None, use_32bit_registry=False):
raise
except win32api.error as exc:
if exc.winerror == 2:
msg = "Cannot find key: {}\\{}".format(local_hive, local_key)
msg = f"Cannot find key: {local_hive}\\{local_key}"
log.trace(exc)
log.trace(msg)
ret["comment"] = msg
ret["success"] = False
elif exc.winerror == 5:
msg = "Access is denied: {}\\{}".format(local_hive, local_key)
msg = f"Access is denied: {local_hive}\\{local_key}"
log.trace(exc)
log.trace(msg)
ret["comment"] = msg
@ -733,7 +732,7 @@ def set_value(
try:
hkey = registry.hkeys[local_hive]
except KeyError:
raise CommandExecutionError("Invalid Hive: {}".format(local_hive))
raise CommandExecutionError(f"Invalid Hive: {local_hive}")
vtype_value = registry.vtype[local_vtype]
access_mask = registry.registry_32[use_32bit_registry] | win32con.KEY_ALL_ACCESS
@ -886,7 +885,7 @@ def delete_key_recursive(hive, key, use_32bit_registry=False):
try:
hkey = registry.hkeys[local_hive]
except KeyError:
raise CommandExecutionError("Invalid Hive: {}".format(local_hive))
raise CommandExecutionError(f"Invalid Hive: {local_hive}")
key_path = local_key
access_mask = registry.registry_32[use_32bit_registry] | win32con.KEY_ALL_ACCESS
@ -918,7 +917,7 @@ def delete_key_recursive(hive, key, use_32bit_registry=False):
"""
_key = win32api.RegOpenKeyEx(_hkey, _keypath, 0, _access_mask)
for subkeyname in _subkeys(_key):
subkeypath = "{}\\{}".format(_keypath, subkeyname)
subkeypath = f"{_keypath}\\{subkeyname}"
_ret = _traverse_registry_tree(_hkey, subkeypath, _ret, access_mask)
_ret.append(subkeypath)
return _ret
@ -938,13 +937,13 @@ def delete_key_recursive(hive, key, use_32bit_registry=False):
key_handle = win32api.RegOpenKeyEx(hkey, sub_key_path, 0, access_mask)
try:
win32api.RegDeleteKey(key_handle, "")
ret["Deleted"].append(r"{}\{}".format(hive, sub_key_path))
ret["Deleted"].append(rf"{hive}\{sub_key_path}")
except OSError as exc:
log.error(exc, exc_info=True)
ret["Failed"].append(r"{}\{} {}".format(hive, sub_key_path, exc))
ret["Failed"].append(rf"{hive}\{sub_key_path} {exc}")
except win32api.error as exc:
log.error(exc, exc_info=True)
ret["Failed"].append(r"{}\{} {}".format(hive, sub_key_path, exc.strerror))
ret["Failed"].append(rf"{hive}\{sub_key_path} {exc.strerror}")
finally:
if key_handle:
win32api.RegCloseKey(key_handle)
@ -998,7 +997,7 @@ def delete_value(hive, key, vname=None, use_32bit_registry=False):
try:
hkey = registry.hkeys[local_hive]
except KeyError:
raise CommandExecutionError("Invalid Hive: {}".format(local_hive))
raise CommandExecutionError(f"Invalid Hive: {local_hive}")
access_mask = registry.registry_32[use_32bit_registry] | win32con.KEY_ALL_ACCESS
handle = None

View file

@ -1,7 +1,14 @@
import shutil
import ssl
import tarfile
import pytest
try:
import trustme
except ImportError:
pass
from pytestshellutils.utils import ports
from saltfactories.utils import random_string
@ -40,6 +47,25 @@ def tinyproxy_pass():
return random_string("tinyproxy-pass-")
@pytest.fixture(scope="session")
def ca():
return trustme.CA()
@pytest.fixture(scope="session")
def httpserver_ssl_context(ca):
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
localhost_cert = ca.issue_cert("127.0.0.1")
localhost_cert.configure_cert(context)
return context
@pytest.fixture(scope="session")
def httpclient_ssl_context(ca):
with ca.cert_pem.tempfile() as ca_temp_path:
return ssl.create_default_context(cafile=ca_temp_path)
@pytest.fixture(params=[True, False], ids=lambda x: "basic-auth" if x else "no-auth")
def tinyproxy_basic_auth(request):
return request.param
@ -107,6 +133,7 @@ def tinyproxy_container(
def test_real_proxy(
tinyproxy_container,
httpserver,
ca,
tinyproxy_port,
tinyproxy_user,
tinyproxy_pass,
@ -137,18 +164,19 @@ def test_real_proxy(
else:
httpserver.expect_request(
"/real_proxy_test",
headers={"X-Tinyproxy-Header": "Test custom tinyproxy header"},
).respond_with_data(data, content_type="application/octet-stream")
url = httpserver.url_for("/real_proxy_test").replace("localhost", "127.0.0.1")
# We just want to be sure that it's using the proxy
ret = salt.utils.http.query(
url,
method=http_method,
data=data,
backend=backend,
opts=opts,
decode_body=False,
)
with ca.cert_pem.tempfile() as ca_temp_path:
ret = salt.utils.http.query(
url,
method=http_method,
data=data,
backend=backend,
opts=opts,
decode_body=False,
verify_ssl=ca_temp_path,
)
body = ret.get("body", "")
assert body == data

View file

@ -4,6 +4,10 @@ Integration tests for the ini_manage state
import pytest
pytestmark = [
pytest.mark.windows_whitelisted,
]
def test_options_present(salt_master, salt_call_cli):
"""

View file

@ -1,6 +1,76 @@
import os
import sys
import salt.modules.ini_manage
import pytest
import salt.modules.ini_manage as ini
import salt.utils.files
import salt.utils.stringutils
@pytest.fixture
def ini_content():
return [
"# Comment on the first line",
"",
"# First main option",
"option1=main1",
"",
"# Second main option",
"option2=main2",
"",
"",
"[main]",
"# Another comment",
"test1=value 1",
"",
"test2=value 2",
"",
"[SectionB]",
"test1=value 1B",
"",
"# Blank line should be above",
"test3 = value 3B",
"",
"[SectionC]",
"# The following option is empty",
"empty_option=",
]
@pytest.fixture(scope="function")
def ini_file(tmp_path, ini_content):
file_path = tmp_path / "file.ini"
yield file_path
@pytest.fixture
def unicode_content():
return [
"# An ini file with some unicode characters",
"",
"[Ascii]",
"de = Deutsch",
"en_GB = English (UK)",
"en_US = English (US)",
"fi = Suomi",
"hu = Magyar",
"it = Italiano",
"nl = Dutch",
"pt = Portuguese",
"sv = Svenska",
"",
"[Юникод]",
"# This means unicode in Russian",
"es = Español",
"es_ES = Español (ES)",
"fr = Français",
"hi = हिंदी",
"ja = 日本語",
"ko = :한국어",
"zh = 简体中文",
"繁體中文 = zh_TW",
]
def test_section_req():
@ -8,4 +78,431 @@ def test_section_req():
Test the __repr__ in the _Section class
"""
expected = "_Section(){}{{}}".format(os.linesep)
assert repr(salt.modules.ini_manage._Section("test")) == expected
assert repr(ini._Section("test")) == expected
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize(
"encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"]
)
def test_get_option(encoding, linesep, ini_file, ini_content):
"""
Test get_option method.
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(ini_content), encoding=encoding
)
ini_file.write_bytes(content)
assert (
ini.get_option(str(ini_file), "main", "test1", encoding=encoding) == "value 1"
)
assert (
ini.get_option(str(ini_file), "main", "test2", encoding=encoding) == "value 2"
)
assert (
ini.get_option(str(ini_file), "SectionB", "test1", encoding=encoding)
== "value 1B"
)
assert (
ini.get_option(str(ini_file), "SectionB", "test3", encoding=encoding)
== "value 3B"
)
assert (
ini.get_option(str(ini_file), "SectionC", "empty_option", encoding=encoding)
== ""
)
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize(
"encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"]
)
def test_get_section(encoding, linesep, ini_file, ini_content):
"""
Test get_section method.
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(ini_content), encoding=encoding
)
ini_file.write_bytes(content)
expected = {"test1": "value 1B", "test3": "value 3B"}
assert ini.get_section(str(ini_file), "SectionB", encoding=encoding) == expected
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize(
"encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"]
)
def test_remove_option(encoding, linesep, ini_file, ini_content):
"""
Test remove_option method.
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(ini_content), encoding=encoding
)
ini_file.write_bytes(content)
assert (
ini.remove_option(str(ini_file), "SectionB", "test1", encoding=encoding)
== "value 1B"
)
assert ini.get_option(str(ini_file), "SectionB", "test1", encoding=encoding) is None
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize(
"encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"]
)
def test_remove_section(encoding, linesep, ini_file, ini_content):
"""
Test remove_section method.
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(ini_content), encoding=encoding
)
ini_file.write_bytes(content)
expected = {"test1": "value 1B", "test3": "value 3B"}
assert ini.remove_section(str(ini_file), "SectionB", encoding=encoding) == expected
assert ini.get_section(str(ini_file), "SectionB", encoding=encoding) == {}
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize(
"encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"]
)
def test_get_ini(encoding, linesep, ini_file, ini_content):
"""
Test get_ini method.
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(ini_content), encoding=encoding
)
ini_file.write_bytes(content)
expected = {
"SectionC": {
"empty_option": "",
},
"SectionB": {
"test1": "value 1B",
"test3": "value 3B",
},
"main": {
"test1": "value 1",
"test2": "value 2",
},
"option2": "main2",
"option1": "main1",
}
assert dict(ini.get_ini(str(ini_file), encoding=encoding)) == expected
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize(
"encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"]
)
def test_set_option(encoding, linesep, ini_file, ini_content):
"""
Test set_option method.
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(ini_content), encoding=encoding
)
ini_file.write_bytes(content)
result = ini.set_option(
str(ini_file),
{
"SectionB": {
"test3": "new value 3B",
"test_set_option": "test_set_value",
},
"SectionD": {"test_set_option2": "test_set_value1"},
},
encoding=encoding,
)
expected = {
"SectionB": {
"test3": {"after": "new value 3B", "before": "value 3B"},
"test_set_option": {"after": "test_set_value", "before": None},
},
"SectionD": {
"after": {"test_set_option2": "test_set_value1"},
"before": None,
},
}
assert result == expected
# Check existing option updated
assert (
ini.get_option(str(ini_file), "SectionB", "test3", encoding=encoding)
== "new value 3B"
)
# Check new section and option added
assert (
ini.get_option(str(ini_file), "SectionD", "test_set_option2", encoding=encoding)
== "test_set_value1"
)
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize(
"encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"]
)
def test_empty_value(encoding, linesep, ini_file, ini_content):
"""
Test empty value preserved after edit
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(ini_content), encoding=encoding
)
ini_file.write_bytes(content)
ini.set_option(
str(ini_file), {"SectionB": {"test3": "new value 3B"}}, encoding=encoding
)
with salt.utils.files.fopen(str(ini_file), "r") as fp_:
file_content = salt.utils.stringutils.to_unicode(fp_.read(), encoding=encoding)
expected = "{0}{1}{0}".format(os.linesep, "empty_option = ")
assert expected in file_content, "empty_option was not preserved"
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize(
"encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"]
)
def test_empty_lines(encoding, linesep, ini_file, ini_content):
"""
Test empty lines preserved after edit
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(ini_content), encoding=encoding
)
ini_file.write_bytes(content)
expected = os.linesep.join(
[
"# Comment on the first line",
"",
"# First main option",
"option1 = main1",
"",
"# Second main option",
"option2 = main2",
"",
"[main]",
"# Another comment",
"test1 = value 1",
"",
"test2 = value 2",
"",
"[SectionB]",
"test1 = value 1B",
"",
"# Blank line should be above",
"test3 = new value 3B",
"",
"[SectionC]",
"# The following option is empty",
"empty_option = ",
"",
]
)
ini.set_option(
str(ini_file), {"SectionB": {"test3": "new value 3B"}}, encoding=encoding
)
with salt.utils.files.fopen(str(ini_file), "r") as fp_:
file_content = fp_.read()
assert expected == file_content
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize(
"encoding", [None, "cp1252" if sys.platform == "win32" else "ISO-2022-JP"]
)
def test_empty_lines_multiple_edits(encoding, linesep, ini_file, ini_content):
"""
Test empty lines preserved after multiple edits
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(ini_content), encoding=encoding
)
ini_file.write_bytes(content)
ini.set_option(
str(ini_file),
{"SectionB": {"test3": "this value will be edited two times"}},
encoding=encoding,
)
expected = os.linesep.join(
[
"# Comment on the first line",
"",
"# First main option",
"option1 = main1",
"",
"# Second main option",
"option2 = main2",
"",
"[main]",
"# Another comment",
"test1 = value 1",
"",
"test2 = value 2",
"",
"[SectionB]",
"test1 = value 1B",
"",
"# Blank line should be above",
"test3 = new value 3B",
"",
"[SectionC]",
"# The following option is empty",
"empty_option = ",
"",
]
)
ini.set_option(
str(ini_file), {"SectionB": {"test3": "new value 3B"}}, encoding=encoding
)
with salt.utils.files.fopen(str(ini_file), "r") as fp_:
file_content = fp_.read()
assert expected == file_content
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize("encoding", [None, "utf-16", "utf-32-le"])
def test_unicode_get_option(encoding, linesep, ini_file, unicode_content):
"""
Test ability to get an option from a file that contains unicode characters
We can't encode the file with something that doesn't support unicode
Ie: cp1252
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(unicode_content), encoding=encoding
)
ini_file.write_bytes(content)
# Get a non-unicode value
assert ini.get_option(str(ini_file), "Ascii", "de", encoding=encoding) == "Deutsch"
# Get a unicode value
assert ini.get_option(str(ini_file), "Юникод", "hi", encoding=encoding) == "हिंदी"
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize("encoding", [None, "utf-16", "utf-16-le", "utf-32-le"])
def test_unicode_set_option(encoding, linesep, ini_file, unicode_content):
"""
Test ability to set an option in a file that contains unicode characters.
The option itself may be unicode
We can't encode the file with something that doesn't support unicode
Ie: cp1252
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(unicode_content), encoding=encoding
)
ini_file.write_bytes(content)
result = ini.set_option(
str(ini_file),
{
"Ascii": {"ay": "Aymar"},
"Юникод": {"dv": "ދިވެހިބަސް"},
},
encoding=encoding,
)
expected = {
"Ascii": {
"ay": {
"before": None,
"after": "Aymar",
},
},
"Юникод": {
"dv": {
"before": None,
"after": "ދިވެހިބަސް",
},
},
}
assert result == expected
# Check existing option updated
assert ini.get_option(str(ini_file), "Ascii", "ay", encoding=encoding) == "Aymar"
# Check new section and option added
assert (
ini.get_option(str(ini_file), "Юникод", "dv", encoding=encoding) == "ދިވެހިބަސް"
)
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize("encoding", [None, "utf-16", "utf-16-le", "utf-32-le"])
def test_unicode_get_section(encoding, linesep, ini_file, unicode_content):
"""
Test get_section method.
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(unicode_content), encoding=encoding
)
ini_file.write_bytes(content)
expected = {
"es": "Español",
"es_ES": "Español (ES)",
"fr": "Français",
"hi": "हिंदी",
"ja": "日本語",
"ko": ":한국어",
"zh": "简体中文",
"繁體中文": "zh_TW",
}
assert ini.get_section(str(ini_file), "Юникод", encoding=encoding) == expected
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize("encoding", [None, "utf-16", "utf-16-le", "utf-32-le"])
def test_unicode_remove_option(encoding, linesep, ini_file, unicode_content):
"""
Test remove_option method.
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(unicode_content), encoding=encoding
)
ini_file.write_bytes(content)
assert (
ini.remove_option(str(ini_file), "Юникод", "繁體中文", encoding=encoding) == "zh_TW"
)
assert ini.get_option(str(ini_file), "Юникод", "繁體中文", encoding=encoding) is None
@pytest.mark.parametrize("linesep", ["\r", "\n", "\r\n"])
@pytest.mark.parametrize("encoding", [None, "utf-16", "utf-16-le", "utf-32-le"])
def test_unicode_remove_section(encoding, linesep, ini_file, unicode_content):
"""
Test remove_section method.
"""
content = salt.utils.stringutils.to_bytes(
linesep.join(unicode_content), encoding=encoding
)
ini_file.write_bytes(content)
expected = {
"es": "Español",
"es_ES": "Español (ES)",
"fr": "Français",
"hi": "हिंदी",
"ja": "日本語",
"ko": ":한국어",
"zh": "简体中文",
"繁體中文": "zh_TW",
}
assert ini.remove_section(str(ini_file), "Юникод", encoding=encoding) == expected
assert ini.get_section(str(ini_file), "Юникод", encoding=encoding) == {}

View file

@ -0,0 +1,6 @@
import sys
def test_salt_system_encoding():
encoding = sys.getdefaultencoding()
assert __salt_system_encoding__ == encoding

View file

@ -250,7 +250,8 @@ def test_query_proxy(httpserver):
)
assert mock_session.return_value.proxies == {
"http": "http://salt_test:super_secret@127.0.0.1:88"
"http": "http://salt_test:super_secret@127.0.0.1:88",
"https": "http://salt_test:super_secret@127.0.0.1:88",
}
opts["no_proxy"] = [httpserver.host]

View file

@ -1,232 +0,0 @@
"""
Testing ini_manage exec module.
"""
import os
import tempfile
import salt.modules.ini_manage as ini
import salt.utils.files
import salt.utils.stringutils
from tests.support.unit import TestCase
class IniManageTestCase(TestCase):
"""
Testing ini_manage exec module.
"""
TEST_FILE_CONTENT = [
"# Comment on the first line",
"",
"# First main option",
"option1=main1",
"",
"# Second main option",
"option2=main2",
"",
"",
"[main]",
"# Another comment",
"test1=value 1",
"",
"test2=value 2",
"",
"[SectionB]",
"test1=value 1B",
"",
"# Blank line should be above",
"test3 = value 3B",
"",
"[SectionC]",
"# The following option is empty",
"empty_option=",
]
maxDiff = None
def _setUp(self, linesep):
self.tfile = tempfile.NamedTemporaryFile(delete=False, mode="w+b")
self.tfile.write(
salt.utils.stringutils.to_bytes(linesep.join(self.TEST_FILE_CONTENT))
)
self.tfile.close()
def setUp(self):
self._setUp(os.linesep)
def tearDown(self):
os.remove(self.tfile.name)
def test_get_option(self):
"""
Test get_option method.
"""
self.assertEqual(ini.get_option(self.tfile.name, "main", "test1"), "value 1")
self.assertEqual(ini.get_option(self.tfile.name, "main", "test2"), "value 2")
self.assertEqual(
ini.get_option(self.tfile.name, "SectionB", "test1"), "value 1B"
)
self.assertEqual(
ini.get_option(self.tfile.name, "SectionB", "test3"), "value 3B"
)
self.assertEqual(
ini.get_option(self.tfile.name, "SectionC", "empty_option"), ""
)
def test_get_section(self):
"""
Test get_section method.
"""
self.assertEqual(
ini.get_section(self.tfile.name, "SectionB"),
{"test1": "value 1B", "test3": "value 3B"},
)
def test_remove_option(self):
"""
Test remove_option method.
"""
self.assertEqual(
ini.remove_option(self.tfile.name, "SectionB", "test1"), "value 1B"
)
self.assertIsNone(ini.get_option(self.tfile.name, "SectionB", "test1"))
def test_remove_section(self):
"""
Test remove_section method.
"""
self.assertEqual(
ini.remove_section(self.tfile.name, "SectionB"),
{"test1": "value 1B", "test3": "value 3B"},
)
self.assertEqual(ini.get_section(self.tfile.name, "SectionB"), {})
def test_get_ini(self):
"""
Test get_ini method.
"""
self.assertEqual(
dict(ini.get_ini(self.tfile.name)),
{
"SectionC": {"empty_option": ""},
"SectionB": {"test1": "value 1B", "test3": "value 3B"},
"main": {"test1": "value 1", "test2": "value 2"},
"option2": "main2",
"option1": "main1",
},
)
def test_set_option(self):
"""
Test set_option method.
"""
result = ini.set_option(
self.tfile.name,
{
"SectionB": {
"test3": "new value 3B",
"test_set_option": "test_set_value",
},
"SectionD": {"test_set_option2": "test_set_value1"},
},
)
self.assertEqual(
result,
{
"SectionB": {
"test3": {"after": "new value 3B", "before": "value 3B"},
"test_set_option": {"after": "test_set_value", "before": None},
},
"SectionD": {
"after": {"test_set_option2": "test_set_value1"},
"before": None,
},
},
)
# Check existing option updated
self.assertEqual(
ini.get_option(self.tfile.name, "SectionB", "test3"), "new value 3B"
)
# Check new section and option added
self.assertEqual(
ini.get_option(self.tfile.name, "SectionD", "test_set_option2"),
"test_set_value1",
)
def test_empty_value(self):
"""
Test empty value preserved after edit
"""
ini.set_option(self.tfile.name, {"SectionB": {"test3": "new value 3B"}})
with salt.utils.files.fopen(self.tfile.name, "r") as fp_:
file_content = salt.utils.stringutils.to_unicode(fp_.read())
expected = "{0}{1}{0}".format(os.linesep, "empty_option = ")
self.assertIn(expected, file_content, "empty_option was not preserved")
def test_empty_lines(self):
"""
Test empty lines preserved after edit
"""
ini.set_option(self.tfile.name, {"SectionB": {"test3": "new value 3B"}})
expected = os.linesep.join(
[
"# Comment on the first line",
"",
"# First main option",
"option1 = main1",
"",
"# Second main option",
"option2 = main2",
"",
"[main]",
"# Another comment",
"test1 = value 1",
"",
"test2 = value 2",
"",
"[SectionB]",
"test1 = value 1B",
"",
"# Blank line should be above",
"test3 = new value 3B",
"",
"[SectionC]",
"# The following option is empty",
"empty_option = ",
"",
]
)
with salt.utils.files.fopen(self.tfile.name, "r") as fp_:
file_content = salt.utils.stringutils.to_unicode(fp_.read())
self.assertEqual(expected, file_content)
def test_empty_lines_multiple_edits(self):
"""
Test empty lines preserved after multiple edits
"""
ini.set_option(
self.tfile.name,
{"SectionB": {"test3": "this value will be edited two times"}},
)
self.test_empty_lines()
def test_newline_characters(self):
"""
Test newline characters
"""
for c in ["\n", "\r", "\r\n"]:
for test in [
self.test_get_option,
self.test_get_section,
self.test_remove_option,
self.test_remove_section,
self.test_get_ini,
self.test_set_option,
self.test_empty_value,
self.test_empty_lines,
self.test_empty_lines_multiple_edits,
]:
self.tearDown()
self._setUp(c)
test()

View file

@ -1438,6 +1438,7 @@ class VM:
env["PYTHONUTF8"] = "1"
env["OUTPUT_COLUMNS"] = str(self.ctx.console.width)
env["GITHUB_ACTIONS_PIPELINE"] = "1"
env["RAISE_DEPRECATIONS_RUNTIME_ERRORS"] = "1"
self.write_and_upload_dot_env(env)
if self.is_windows is False and self.config.ssh_username != "root":
sudo = True