Merge 3006.x into master

This commit is contained in:
Pedro Algarvio 2023-11-28 17:46:50 +00:00
commit f60c87aacb
No known key found for this signature in database
GPG key ID: BB36BF6584A298FF
53 changed files with 1873 additions and 1073 deletions

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

@ -0,0 +1 @@
Fixed merging of complex pillar overrides with salt-ssh states

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

@ -0,0 +1 @@
Fixed gpg pillar rendering with salt-ssh

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

@ -0,0 +1 @@
Made salt-ssh states not re-render pillars unnecessarily

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

@ -0,0 +1 @@
Ensured the pillar in SSH wrapper modules is the same as the one used in template rendering when overrides are passed

View file

@ -1 +1 @@
centosstream-9-x86_64: ami-0df4c4ee0d3a417e6
centosstream-9-x86_64: ami-0793e1741f291eaf9

View file

@ -1,8 +1,8 @@
{
"almalinux-8-arm64": {
"ami": "ami-01701198f23cc656f",
"ami": "ami-0a2b327b74836f618",
"ami_description": "CI Image of AlmaLinux 8 arm64",
"ami_name": "salt-project/ci/almalinux/8/arm64/20231019.0610",
"ami_name": "salt-project/ci/almalinux/8/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -10,9 +10,9 @@
"ssh_username": "ec2-user"
},
"almalinux-8": {
"ami": "ami-0d1fa37788a762561",
"ami": "ami-03d4319831692a030",
"ami_description": "CI Image of AlmaLinux 8 x86_64",
"ami_name": "salt-project/ci/almalinux/8/x86_64/20231019.0610",
"ami_name": "salt-project/ci/almalinux/8/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -20,9 +20,9 @@
"ssh_username": "ec2-user"
},
"almalinux-9-arm64": {
"ami": "ami-0690d2b725982ad83",
"ami": "ami-01e0f60c59c6fe8f3",
"ami_description": "CI Image of AlmaLinux 9 arm64",
"ami_name": "salt-project/ci/almalinux/9/arm64/20231019.0610",
"ami_name": "salt-project/ci/almalinux/9/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -30,9 +30,9 @@
"ssh_username": "ec2-user"
},
"almalinux-9": {
"ami": "ami-0ffb222eea4b1c4ee",
"ami": "ami-0dbbac81b50ebb8b4",
"ami_description": "CI Image of AlmaLinux 9 x86_64",
"ami_name": "salt-project/ci/almalinux/9/x86_64/20231019.0610",
"ami_name": "salt-project/ci/almalinux/9/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -40,9 +40,9 @@
"ssh_username": "ec2-user"
},
"amazonlinux-2-arm64": {
"ami": "ami-0e9521385f61055a0",
"ami": "ami-05cc59dcbf59085f1",
"ami_description": "CI Image of AmazonLinux 2 arm64",
"ami_name": "salt-project/ci/amazonlinux/2/arm64/20231019.0610",
"ami_name": "salt-project/ci/amazonlinux/2/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -50,9 +50,9 @@
"ssh_username": "ec2-user"
},
"amazonlinux-2": {
"ami": "ami-038cc290cd0dd2fb3",
"ami": "ami-07f715092c8ed2451",
"ami_description": "CI Image of AmazonLinux 2 x86_64",
"ami_name": "salt-project/ci/amazonlinux/2/x86_64/20231019.0610",
"ami_name": "salt-project/ci/amazonlinux/2/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -60,9 +60,9 @@
"ssh_username": "ec2-user"
},
"amazonlinux-2023-arm64": {
"ami": "ami-00aadf98a51c60684",
"ami": "ami-074502af4314eb812",
"ami_description": "CI Image of AmazonLinux 2023 arm64",
"ami_name": "salt-project/ci/amazonlinux/2023/arm64/20231019.0611",
"ami_name": "salt-project/ci/amazonlinux/2023/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -70,9 +70,9 @@
"ssh_username": "ec2-user"
},
"amazonlinux-2023": {
"ami": "ami-0aeb34a1da784672c",
"ami": "ami-0a1059334d3373321",
"ami_description": "CI Image of AmazonLinux 2023 x86_64",
"ami_name": "salt-project/ci/amazonlinux/2023/x86_64/20231019.0611",
"ami_name": "salt-project/ci/amazonlinux/2023/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -80,9 +80,9 @@
"ssh_username": "ec2-user"
},
"archlinux-lts": {
"ami": "ami-0b4ab49118d17c567",
"ami": "ami-0430452d2dfbb8f4b",
"ami_description": "CI Image of ArchLinux lts x86_64",
"ami_name": "salt-project/ci/archlinux/lts/x86_64/20231019.0610",
"ami_name": "salt-project/ci/archlinux/lts/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "false",
"instance_type": "t3a.large",
@ -90,9 +90,9 @@
"ssh_username": "arch"
},
"centos-7-arm64": {
"ami": "ami-0712b87973da8b106",
"ami": "ami-0fc26a930a59d1417",
"ami_description": "CI Image of CentOS 7 arm64",
"ami_name": "salt-project/ci/centos/7/arm64/20231019.0611",
"ami_name": "salt-project/ci/centos/7/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -100,9 +100,9 @@
"ssh_username": "centos"
},
"centos-7": {
"ami": "ami-0432ac4d81ff9c6d7",
"ami": "ami-0532c2c5f18771fa8",
"ami_description": "CI Image of CentOS 7 x86_64",
"ami_name": "salt-project/ci/centos/7/x86_64/20231019.0610",
"ami_name": "salt-project/ci/centos/7/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -110,9 +110,9 @@
"ssh_username": "centos"
},
"centosstream-8-arm64": {
"ami": "ami-00819771fc6d6f37a",
"ami": "ami-0916df690c02e0af0",
"ami_description": "CI Image of CentOSStream 8 arm64",
"ami_name": "salt-project/ci/centosstream/8/arm64/20231019.0610",
"ami_name": "salt-project/ci/centosstream/8/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -120,9 +120,9 @@
"ssh_username": "centos"
},
"centosstream-8": {
"ami": "ami-00d0ebd1ad30509fc",
"ami": "ami-06cf36f0232c681e2",
"ami_description": "CI Image of CentOSStream 8 x86_64",
"ami_name": "salt-project/ci/centosstream/8/x86_64/20231019.0610",
"ami_name": "salt-project/ci/centosstream/8/x86_64/20231126.1416",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -130,9 +130,9 @@
"ssh_username": "centos"
},
"centosstream-9-arm64": {
"ami": "ami-08599182d0e9788f9",
"ami": "ami-094e17e254aa77811",
"ami_description": "CI Image of CentOSStream 9 arm64",
"ami_name": "salt-project/ci/centosstream/9/arm64/20231019.0610",
"ami_name": "salt-project/ci/centosstream/9/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -140,9 +140,9 @@
"ssh_username": "ec2-user"
},
"centosstream-9": {
"ami": "ami-0df4c4ee0d3a417e6",
"ami": "ami-0793e1741f291eaf9",
"ami_description": "CI Image of CentOSStream 9 x86_64",
"ami_name": "salt-project/ci/centosstream/9/x86_64/20231019.0610",
"ami_name": "salt-project/ci/centosstream/9/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -150,9 +150,9 @@
"ssh_username": "ec2-user"
},
"debian-10-arm64": {
"ami": "ami-0be576b80116655d6",
"ami": "ami-0c0b1bdab1b3c9733",
"ami_description": "CI Image of Debian 10 arm64",
"ami_name": "salt-project/ci/debian/10/arm64/20231019.0611",
"ami_name": "salt-project/ci/debian/10/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "false",
"instance_type": "m6g.large",
@ -160,9 +160,9 @@
"ssh_username": "admin"
},
"debian-10": {
"ami": "ami-0dc775a61113efde0",
"ami": "ami-082605fda5afd9131",
"ami_description": "CI Image of Debian 10 x86_64",
"ami_name": "salt-project/ci/debian/10/x86_64/20231019.0611",
"ami_name": "salt-project/ci/debian/10/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -170,9 +170,9 @@
"ssh_username": "admin"
},
"debian-11-arm64": {
"ami": "ami-086e42800d155779f",
"ami": "ami-0df6946d840d24ced",
"ami_description": "CI Image of Debian 11 arm64",
"ami_name": "salt-project/ci/debian/11/arm64/20231019.0611",
"ami_name": "salt-project/ci/debian/11/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "false",
"instance_type": "m6g.large",
@ -180,9 +180,9 @@
"ssh_username": "admin"
},
"debian-11": {
"ami": "ami-01b730ce9083afb7b",
"ami": "ami-0c2198080c953861d",
"ami_description": "CI Image of Debian 11 x86_64",
"ami_name": "salt-project/ci/debian/11/x86_64/20231019.0611",
"ami_name": "salt-project/ci/debian/11/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -190,9 +190,9 @@
"ssh_username": "admin"
},
"debian-12-arm64": {
"ami": "ami-0a8fb0c54e8ac78c3",
"ami": "ami-050b69eb0e0a66373",
"ami_description": "CI Image of Debian 12 arm64",
"ami_name": "salt-project/ci/debian/12/arm64/20231019.0611",
"ami_name": "salt-project/ci/debian/12/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "false",
"instance_type": "m6g.large",
@ -200,9 +200,9 @@
"ssh_username": "admin"
},
"debian-12": {
"ami": "ami-09736ea89f5625680",
"ami": "ami-032e397b97865f83e",
"ami_description": "CI Image of Debian 12 x86_64",
"ami_name": "salt-project/ci/debian/12/x86_64/20231019.0611",
"ami_name": "salt-project/ci/debian/12/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -210,9 +210,9 @@
"ssh_username": "admin"
},
"fedora-37-arm64": {
"ami": "ami-067631a1bb1d3d6e4",
"ami": "ami-0000739b5d4971ba1",
"ami_description": "CI Image of Fedora 37 arm64",
"ami_name": "salt-project/ci/fedora/37/arm64/20231019.0630",
"ami_name": "salt-project/ci/fedora/37/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -220,9 +220,9 @@
"ssh_username": "fedora"
},
"fedora-37": {
"ami": "ami-03dab52e75c1d7594",
"ami": "ami-086af8fe37696acd6",
"ami_description": "CI Image of Fedora 37 x86_64",
"ami_name": "salt-project/ci/fedora/37/x86_64/20231019.0630",
"ami_name": "salt-project/ci/fedora/37/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -230,9 +230,9 @@
"ssh_username": "fedora"
},
"fedora-38-arm64": {
"ami": "ami-0a67ad5dc0b4e67a9",
"ami": "ami-0a078cdd3a57ef342",
"ami_description": "CI Image of Fedora 38 arm64",
"ami_name": "salt-project/ci/fedora/38/arm64/20231019.0630",
"ami_name": "salt-project/ci/fedora/38/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -240,9 +240,9 @@
"ssh_username": "fedora"
},
"fedora-38": {
"ami": "ami-00e8299d247d3bfb9",
"ami": "ami-0566e7f93c5cf6afc",
"ami_description": "CI Image of Fedora 38 x86_64",
"ami_name": "salt-project/ci/fedora/38/x86_64/20231019.0630",
"ami_name": "salt-project/ci/fedora/38/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -250,9 +250,9 @@
"ssh_username": "fedora"
},
"opensuse-15": {
"ami": "ami-0fa4ce121739032e2",
"ami": "ami-0c64c574d488d33f6",
"ami_description": "CI Image of Opensuse 15 x86_64",
"ami_name": "salt-project/ci/opensuse/15/x86_64/20231019.0611",
"ami_name": "salt-project/ci/opensuse/15/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -260,9 +260,9 @@
"ssh_username": "ec2-user"
},
"photonos-3-arm64": {
"ami": "ami-09687bbdca9322cfd",
"ami": "ami-03ac724168ce02eed",
"ami_description": "CI Image of PhotonOS 3 arm64",
"ami_name": "salt-project/ci/photonos/3/arm64/20231019.0626",
"ami_name": "salt-project/ci/photonos/3/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -270,9 +270,9 @@
"ssh_username": "root"
},
"photonos-3": {
"ami": "ami-0e29021a535519231",
"ami": "ami-0072dfd1f7bc5f586",
"ami_description": "CI Image of PhotonOS 3 x86_64",
"ami_name": "salt-project/ci/photonos/3/x86_64/20231019.0626",
"ami_name": "salt-project/ci/photonos/3/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -280,9 +280,9 @@
"ssh_username": "root"
},
"photonos-4-arm64": {
"ami": "ami-06a0418b67a9ec332",
"ami": "ami-05a215fe4cf29227b",
"ami_description": "CI Image of PhotonOS 4 arm64",
"ami_name": "salt-project/ci/photonos/4/arm64/20231019.0626",
"ami_name": "salt-project/ci/photonos/4/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -290,9 +290,9 @@
"ssh_username": "root"
},
"photonos-4": {
"ami": "ami-08ae023a2755a60dc",
"ami": "ami-06addda42fc8c5db3",
"ami_description": "CI Image of PhotonOS 4 x86_64",
"ami_name": "salt-project/ci/photonos/4/x86_64/20231019.0626",
"ami_name": "salt-project/ci/photonos/4/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -300,9 +300,9 @@
"ssh_username": "root"
},
"photonos-5-arm64": {
"ami": "ami-05b3dd82b94e82736",
"ami": "ami-0e78012df225dbe96",
"ami_description": "CI Image of PhotonOS 5 arm64",
"ami_name": "salt-project/ci/photonos/5/arm64/20231019.0627",
"ami_name": "salt-project/ci/photonos/5/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -310,9 +310,9 @@
"ssh_username": "root"
},
"photonos-5": {
"ami": "ami-016991d4c267732c3",
"ami": "ami-0fc61f964bc262714",
"ami_description": "CI Image of PhotonOS 5 x86_64",
"ami_name": "salt-project/ci/photonos/5/x86_64/20231019.0627",
"ami_name": "salt-project/ci/photonos/5/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -320,9 +320,9 @@
"ssh_username": "root"
},
"ubuntu-20.04-arm64": {
"ami": "ami-0dc851d4db96c052b",
"ami": "ami-0cc504307b587cd77",
"ami_description": "CI Image of Ubuntu 20.04 arm64",
"ami_name": "salt-project/ci/ubuntu/20.04/arm64/20231019.0628",
"ami_name": "salt-project/ci/ubuntu/20.04/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -330,9 +330,9 @@
"ssh_username": "ubuntu"
},
"ubuntu-20.04": {
"ami": "ami-05c262fca2254d2cb",
"ami": "ami-03376fca39f6d9186",
"ami_description": "CI Image of Ubuntu 20.04 x86_64",
"ami_name": "salt-project/ci/ubuntu/20.04/x86_64/20231019.0627",
"ami_name": "salt-project/ci/ubuntu/20.04/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -340,9 +340,9 @@
"ssh_username": "ubuntu"
},
"ubuntu-22.04-arm64": {
"ami": "ami-007415ef606318020",
"ami": "ami-0be361d529bb46410",
"ami_description": "CI Image of Ubuntu 22.04 arm64",
"ami_name": "salt-project/ci/ubuntu/22.04/arm64/20231019.0628",
"ami_name": "salt-project/ci/ubuntu/22.04/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -350,9 +350,9 @@
"ssh_username": "ubuntu"
},
"ubuntu-22.04": {
"ami": "ami-04d01b95ca8570ed3",
"ami": "ami-0c9d29f29868da4ce",
"ami_description": "CI Image of Ubuntu 22.04 x86_64",
"ami_name": "salt-project/ci/ubuntu/22.04/x86_64/20231019.0628",
"ami_name": "salt-project/ci/ubuntu/22.04/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -360,9 +360,9 @@
"ssh_username": "ubuntu"
},
"ubuntu-23.04-arm64": {
"ami": "ami-0da01b22cca0f4281",
"ami": "ami-0b80ab7ead3c7d289",
"ami_description": "CI Image of Ubuntu 23.04 arm64",
"ami_name": "salt-project/ci/ubuntu/23.04/arm64/20231019.0629",
"ami_name": "salt-project/ci/ubuntu/23.04/arm64/20231126.1417",
"arch": "arm64",
"cloudwatch-agent-available": "true",
"instance_type": "m6g.large",
@ -370,9 +370,9 @@
"ssh_username": "ubuntu"
},
"ubuntu-23.04": {
"ami": "ami-03e32d8e9ccc6cd6a",
"ami": "ami-0d17dce1842e37811",
"ami_description": "CI Image of Ubuntu 23.04 x86_64",
"ami_name": "salt-project/ci/ubuntu/23.04/x86_64/20231019.0629",
"ami_name": "salt-project/ci/ubuntu/23.04/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.large",
@ -380,9 +380,9 @@
"ssh_username": "ubuntu"
},
"windows-2016": {
"ami": "ami-02fd868528f2c7a62",
"ami": "ami-043db64b3b46a804c",
"ami_description": "CI Image of Windows 2016 x86_64",
"ami_name": "salt-project/ci/windows/2016/x86_64/20231019.0610",
"ami_name": "salt-project/ci/windows/2016/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.xlarge",
@ -390,9 +390,9 @@
"ssh_username": "Administrator"
},
"windows-2019": {
"ami": "ami-0d6f2b5a109c98224",
"ami": "ami-0f7a8dc8862bff13f",
"ami_description": "CI Image of Windows 2019 x86_64",
"ami_name": "salt-project/ci/windows/2019/x86_64/20231019.0610",
"ami_name": "salt-project/ci/windows/2019/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.xlarge",
@ -400,9 +400,9 @@
"ssh_username": "Administrator"
},
"windows-2022": {
"ami": "ami-013e3141df4b2418f",
"ami": "ami-05a829f3649aa33d6",
"ami_description": "CI Image of Windows 2022 x86_64",
"ami_name": "salt-project/ci/windows/2022/x86_64/20231019.0610",
"ami_name": "salt-project/ci/windows/2022/x86_64/20231126.1417",
"arch": "x86_64",
"cloudwatch-agent-available": "true",
"instance_type": "t3a.xlarge",

View file

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

View file

@ -40,7 +40,7 @@ pycparser==2.21
# via
# -c requirements/static/ci/py3.10/linux.txt
# cffi
pypsexec==0.3.0
pypsexec==0.1.0
# via -r requirements/static/ci/cloud.in
pyspnego==0.9.0
# via
@ -62,6 +62,7 @@ six==1.16.0
# via
# -c requirements/static/ci/py3.10/linux.txt
# profitbricks
# pypsexec
# pywinrm
smbprotocol==1.10.1
# via

View file

@ -320,6 +320,8 @@ pydantic==1.10.8
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/darwin.in
pynacl==1.5.0

View file

@ -324,6 +324,8 @@ pydantic==1.10.8
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pyinotify==0.9.6 ; sys_platform != "win32" and sys_platform != "darwin" and platform_system != "openbsd"
# via -r requirements/static/ci/common.in
pynacl==1.5.0

View file

@ -351,6 +351,8 @@ pydantic==1.10.8
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/linux.in
pyiface==0.0.11

View file

@ -275,6 +275,8 @@ pydantic==1.10.8
# via
# -c requirements/static/ci/../pkg/py3.10/windows.txt
# inflect
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/windows.in
pymssql==2.2.7 ; sys_platform == "win32"

View file

@ -34,8 +34,6 @@ netaddr==0.8.0
# via
# -c requirements/static/ci/py3.11/linux.txt
# -r requirements/static/ci/cloud.in
ntlm-auth==1.3.0
# via requests-ntlm
profitbricks==4.1.3
# via -r requirements/static/ci/cloud.in
pycparser==2.21
@ -44,13 +42,14 @@ pycparser==2.21
# cffi
pypsexec==0.1.0
# via -r requirements/static/ci/cloud.in
pyspnego==0.8.0
pyspnego==0.9.0
# via
# -r requirements/static/ci/cloud.in
# requests-ntlm
# smbprotocol
pywinrm==0.3.0
pywinrm==0.4.3
# via -r requirements/static/ci/cloud.in
requests-ntlm==1.1.0
requests-ntlm==1.2.0
# via pywinrm
requests==2.31.0
# via

View file

@ -324,6 +324,8 @@ pydantic==2.5.2
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/darwin.in
pynacl==1.5.0

View file

@ -328,6 +328,8 @@ pydantic==2.5.2
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pyinotify==0.9.6 ; sys_platform != "win32" and sys_platform != "darwin" and platform_system != "openbsd"
# via -r requirements/static/ci/common.in
pynacl==1.5.0

View file

@ -353,6 +353,8 @@ pydantic==2.5.2
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/linux.in
pyiface==0.0.11

View file

@ -94,6 +94,7 @@ cryptography==41.0.5
# etcd3-py
# moto
# pyopenssl
# pyspnego
# requests-ntlm
distlib==0.3.7
# via virtualenv
@ -229,8 +230,6 @@ multidict==6.0.2
# via
# aiohttp
# yarl
ntlm-auth==1.5.0
# via requests-ntlm
packaging==23.1
# via
# -c requirements/static/ci/../pkg/py3.11/windows.txt
@ -280,6 +279,8 @@ pydantic==2.5.2
# via
# -c requirements/static/ci/../pkg/py3.11/windows.txt
# inflect
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/windows.in
pymssql==2.2.7 ; sys_platform == "win32"
@ -299,6 +300,8 @@ pyopenssl==23.2.0
# etcd3-py
pyrsistent==0.19.3
# via jsonschema
pyspnego==0.10.2
# via requests-ntlm
pytest-custom-exit-code==0.3.0
# via -r requirements/pytest.txt
pytest-helpers-namespace==2021.12.29
@ -364,7 +367,7 @@ pywin32==305 ; sys_platform == "win32"
# docker
# pytest-skip-markers
# wmi
pywinrm==0.4.1
pywinrm==0.4.3
# via -r requirements/static/ci/windows.in
pyyaml==6.0.1
# via
@ -380,7 +383,7 @@ pyzmq==25.1.1
# -c requirements/static/ci/../pkg/py3.11/windows.txt
# -r requirements/zeromq.txt
# pytest-salt-factories
requests-ntlm==1.1.0
requests-ntlm==1.2.0
# via pywinrm
requests==2.31.0
# via
@ -429,6 +432,8 @@ smmap==5.0.0
# via gitdb
sqlparse==0.4.4
# via -r requirements/static/ci/common.in
sspilib==0.1.0
# via pyspnego
strict-rfc3339==0.7
# via -r requirements/static/ci/common.in
tempora==5.3.0

View file

@ -381,8 +381,6 @@ ntc-templates==4.0.1
# via
# -c requirements/static/ci/py3.12/linux.txt
# netmiko
ntlm-auth==1.3.0
# via requests-ntlm
oscrypto==1.3.0
# via
# -c requirements/static/ci/py3.12/linux.txt
@ -463,6 +461,10 @@ pyeapi==1.0.0
# via
# -c requirements/static/ci/py3.12/linux.txt
# napalm
pyfakefs==5.3.1
# via
# -c requirements/static/ci/py3.12/linux.txt
# -r requirements/pytest.txt
pyinotify==0.9.6 ; sys_platform != "win32" and sys_platform != "darwin" and platform_system != "openbsd"
# via
# -c requirements/static/ci/py3.12/linux.txt
@ -493,9 +495,10 @@ pyserial==3.5
# -c requirements/static/ci/py3.12/linux.txt
# junos-eznc
# netmiko
pyspnego==0.8.0
pyspnego==0.9.0
# via
# -r requirements/static/ci/cloud.in
# requests-ntlm
# smbprotocol
pytest-custom-exit-code==0.3.0
# via
@ -577,7 +580,7 @@ pyvmomi==8.0.1.0.1
# via
# -c requirements/static/ci/py3.12/linux.txt
# -r requirements/static/ci/common.in
pywinrm==0.3.0
pywinrm==0.4.3
# via -r requirements/static/ci/cloud.in
pyyaml==6.0.1
# via
@ -598,7 +601,7 @@ pyzmq==25.1.1
# -c requirements/static/ci/py3.12/linux.txt
# -r requirements/zeromq.txt
# pytest-salt-factories
requests-ntlm==1.1.0
requests-ntlm==1.2.0
# via pywinrm
requests==2.31.0
# via

View file

@ -324,6 +324,8 @@ pydantic==2.5.2
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/darwin.in
pynacl==1.5.0

View file

@ -328,6 +328,8 @@ pydantic==2.5.2
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pyinotify==0.9.6 ; sys_platform != "win32" and sys_platform != "darwin" and platform_system != "openbsd"
# via -r requirements/static/ci/common.in
pynacl==1.5.0

View file

@ -353,6 +353,8 @@ pydantic==2.5.2
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/linux.in
pyiface==0.0.11

View file

@ -273,6 +273,7 @@ pycryptodomex==3.10.1
# via
# -c requirements/static/ci/../pkg/py3.11/windows.txt
# -r requirements/crypto.txt
pydantic-core==2.14.5
# via
# -c requirements/static/ci/../pkg/py3.11/windows.txt
@ -281,6 +282,11 @@ pydantic==2.5.2
# via
# -c requirements/static/ci/../pkg/py3.11/windows.txt
# inflect
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/windows.in
pymssql==2.2.7 ; sys_platform == "win32"
@ -366,7 +372,7 @@ pywin32==306 ; sys_platform == "win32"
# docker
# pytest-skip-markers
# wmi
pywinrm==0.4.1
pywinrm==0.4.3
# via -r requirements/static/ci/windows.in
pyyaml==6.0.1
# via
@ -381,7 +387,7 @@ pyzmq==25.1.1
# -c requirements/static/ci/../pkg/py3.11/windows.txt
# -r requirements/zeromq.txt
# pytest-salt-factories
requests-ntlm==1.1.0
requests-ntlm==1.2.0
# via pywinrm
requests==2.31.0
# via

View file

@ -40,7 +40,7 @@ pycparser==2.21
# via
# -c requirements/static/ci/py3.8/linux.txt
# cffi
pypsexec==0.3.0
pypsexec==0.1.0
# via -r requirements/static/ci/cloud.in
pyspnego==0.9.0
# via
@ -62,6 +62,7 @@ six==1.16.0
# via
# -c requirements/static/ci/py3.8/linux.txt
# profitbricks
# pypsexec
# pywinrm
smbprotocol==1.10.1
# via

View file

@ -328,6 +328,8 @@ pydantic==1.10.8
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pyinotify==0.9.6 ; sys_platform != "win32" and sys_platform != "darwin" and platform_system != "openbsd"
# via -r requirements/static/ci/common.in
pynacl==1.5.0

View file

@ -355,6 +355,8 @@ pydantic==1.10.8
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/linux.in
pyiface==0.0.11

View file

@ -279,6 +279,8 @@ pydantic==1.10.8
# via
# -c requirements/static/ci/../pkg/py3.8/windows.txt
# inflect
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/windows.in
pymssql==2.2.7 ; sys_platform == "win32"

View file

@ -40,7 +40,7 @@ pycparser==2.21
# via
# -c requirements/static/ci/py3.9/linux.txt
# cffi
pypsexec==0.3.0
pypsexec==0.1.0
# via -r requirements/static/ci/cloud.in
pyspnego==0.9.0
# via
@ -62,6 +62,7 @@ six==1.16.0
# via
# -c requirements/static/ci/py3.9/linux.txt
# profitbricks
# pypsexec
# pywinrm
smbprotocol==1.10.1
# via

View file

@ -320,6 +320,8 @@ pydantic==1.10.8
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/darwin.in
pynacl==1.5.0

View file

@ -324,6 +324,8 @@ pydantic==1.10.8
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pyinotify==0.9.6 ; sys_platform != "win32" and sys_platform != "darwin" and platform_system != "openbsd"
# via -r requirements/static/ci/common.in
pynacl==1.5.0

View file

@ -353,6 +353,8 @@ pydantic==1.10.8
# inflect
pyeapi==1.0.0
# via napalm
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/linux.in
pyiface==0.0.11

View file

@ -275,6 +275,8 @@ pydantic==1.10.8
# via
# -c requirements/static/ci/../pkg/py3.9/windows.txt
# inflect
pyfakefs==5.3.1
# via -r requirements/pytest.txt
pygit2==1.13.1
# via -r requirements/static/ci/windows.in
pymssql==2.2.7 ; sys_platform == "win32"

View file

@ -93,6 +93,17 @@ warnings.filterwarnings(
category=DeprecationWarning,
)
warnings.filterwarnings(
"ignore",
"Deprecated call to `pkg_resources.declare_namespace.*",
category=DeprecationWarning,
)
warnings.filterwarnings(
"ignore",
".*pkg_resources is deprecated as an API.*",
category=DeprecationWarning,
)
def __define_global_system_encoding_variable__():
# This is the most trustworthy source of the system encoding, though, if

View file

@ -1196,9 +1196,11 @@ class Single:
for grain in self.target["grains"]:
opts_pkg["grains"][grain] = self.target["grains"][grain]
# Pillar compilation needs the master opts primarily,
# same as during regular operation.
popts = {}
popts.update(opts_pkg["__master_opts__"])
popts.update(opts_pkg)
popts.update(opts_pkg["__master_opts__"])
pillar = salt.pillar.Pillar(
popts,
opts_pkg["grains"],

View file

@ -31,10 +31,17 @@ class SSHState(salt.state.State):
Create a State object which wraps the SSH functions for state operations
"""
def __init__(self, opts, pillar=None, wrapper=None, context=None):
def __init__(
self,
opts,
pillar_override=None,
wrapper=None,
context=None,
initial_pillar=None,
):
self.wrapper = wrapper
self.context = context
super().__init__(opts, pillar)
super().__init__(opts, pillar_override, initial_pillar=initial_pillar)
def load_modules(self, data=None, proxy=None):
"""
@ -49,6 +56,28 @@ class SSHState(salt.state.State):
)
self.rend = salt.loader.render(self.opts, self.functions)
def _gather_pillar(self):
"""
The opts used during pillar rendering should contain the master
opts in the root namespace. self.opts is the modified minion opts,
containing the original master opts in __master_opts__.
"""
_opts = self.opts
popts = {}
# Pillar compilation needs the master opts primarily,
# same as during regular operation.
popts.update(_opts)
popts.update(_opts.get("__master_opts__", {}))
# But, salt.state.State takes the parameters for get_pillar from
# the opts, so we need to ensure they are correct for the minion.
popts["id"] = _opts["id"]
popts["saltenv"] = _opts["saltenv"]
popts["pillarenv"] = _opts.get("pillarenv")
self.opts = popts
pillar = super()._gather_pillar()
self.opts = _opts
return pillar
def check_refresh(self, data, ret):
"""
Stub out check_refresh
@ -69,10 +98,24 @@ class SSHHighState(salt.state.BaseHighState):
stack = []
def __init__(self, opts, pillar=None, wrapper=None, fsclient=None, context=None):
def __init__(
self,
opts,
pillar_override=None,
wrapper=None,
fsclient=None,
context=None,
initial_pillar=None,
):
self.client = fsclient
salt.state.BaseHighState.__init__(self, opts)
self.state = SSHState(opts, pillar, wrapper, context=context)
self.state = SSHState(
opts,
pillar_override,
wrapper,
context=context,
initial_pillar=initial_pillar,
)
self.matchers = salt.loader.matchers(self.opts)
self._pydsl_all_decls = {}

View file

@ -28,7 +28,7 @@ __func_alias__ = {"apply_": "apply"}
log = logging.getLogger(__name__)
def _ssh_state(chunks, st_kwargs, kwargs, test=False):
def _ssh_state(chunks, st_kwargs, kwargs, pillar, test=False):
"""
Function to run a state with the given chunk via salt-ssh
"""
@ -43,7 +43,7 @@ def _ssh_state(chunks, st_kwargs, kwargs, test=False):
__context__["fileclient"],
chunks,
file_refs,
__pillar__.value(),
pillar,
st_kwargs["id_"],
)
trans_tar_sum = salt.utils.hashutils.get_hash(trans_tar, __opts__["hash_type"])
@ -173,21 +173,30 @@ def sls(mods, saltenv="base", test=None, exclude=None, **kwargs):
"""
st_kwargs = __salt__.kwargs
__opts__["grains"] = __grains__.value()
__pillar__.update(kwargs.get("pillar", {}))
opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
opts["test"] = _get_test_value(test, **kwargs)
initial_pillar = _get_initial_pillar(opts)
pillar_override = kwargs.get("pillar")
with salt.client.ssh.state.SSHHighState(
opts,
__pillar__.value(),
pillar_override,
__salt__.value(),
__context__["fileclient"],
context=__context__.value(),
initial_pillar=initial_pillar,
) as st_:
if not _check_pillar(kwargs, st_.opts["pillar"]):
__context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE
err = ["Pillar failed to render with the following messages:"]
err += st_.opts["pillar"]["_errors"]
return err
try:
pillar = st_.opts["pillar"].value()
except AttributeError:
pillar = st_.opts["pillar"]
if pillar_override is not None or initial_pillar is None:
# Ensure other wrappers use the correct pillar
__pillar__.update(pillar)
st_.push_active()
mods = _parse_mods(mods)
high_data, errors = st_.render_highstate(
@ -231,7 +240,7 @@ def sls(mods, saltenv="base", test=None, exclude=None, **kwargs):
__context__["fileclient"],
chunks,
file_refs,
__pillar__.value(),
pillar,
st_kwargs["id_"],
roster_grains,
)
@ -329,12 +338,7 @@ def _check_queue(queue, kwargs):
def _get_initial_pillar(opts):
return (
__pillar__
if __opts__["__cli"] == "salt-call"
and opts["pillarenv"] == __opts__["pillarenv"]
else None
)
return __pillar__.value() if opts["pillarenv"] == __opts__["pillarenv"] else None
def low(data, **kwargs):
@ -353,10 +357,11 @@ def low(data, **kwargs):
chunks = [data]
with salt.client.ssh.state.SSHHighState(
__opts__,
__pillar__.value(),
None,
__salt__.value(),
__context__["fileclient"],
context=__context__.value(),
initial_pillar=__pillar__.value(),
) as st_:
for chunk in chunks:
chunk["__id__"] = (
@ -440,17 +445,26 @@ def high(data, **kwargs):
salt '*' state.high '{"vim": {"pkg": ["installed"]}}'
"""
__pillar__.update(kwargs.get("pillar", {}))
st_kwargs = __salt__.kwargs
__opts__["grains"] = __grains__.value()
opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
pillar_override = kwargs.get("pillar")
initial_pillar = _get_initial_pillar(opts)
with salt.client.ssh.state.SSHHighState(
opts,
__pillar__.value(),
pillar_override,
__salt__.value(),
__context__["fileclient"],
context=__context__.value(),
initial_pillar=initial_pillar,
) as st_:
try:
pillar = st_.opts["pillar"].value()
except AttributeError:
pillar = st_.opts["pillar"]
if pillar_override is not None or initial_pillar is None:
# Ensure other wrappers use the correct pillar
__pillar__.update(pillar)
st_.push_active()
chunks = st_.state.compile_high_data(data)
file_refs = salt.client.ssh.state.lowstate_file_refs(
@ -469,7 +483,7 @@ def high(data, **kwargs):
__context__["fileclient"],
chunks,
file_refs,
__pillar__.value(),
pillar,
st_kwargs["id_"],
roster_grains,
)
@ -677,23 +691,32 @@ def highstate(test=None, **kwargs):
salt '*' state.highstate exclude=sls_to_exclude
salt '*' state.highstate exclude="[{'id': 'id_to_exclude'}, {'sls': 'sls_to_exclude'}]"
"""
__pillar__.update(kwargs.get("pillar", {}))
st_kwargs = __salt__.kwargs
__opts__["grains"] = __grains__.value()
opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
opts["test"] = _get_test_value(test, **kwargs)
pillar_override = kwargs.get("pillar")
initial_pillar = _get_initial_pillar(opts)
with salt.client.ssh.state.SSHHighState(
opts,
__pillar__.value(),
pillar_override,
__salt__.value(),
__context__["fileclient"],
context=__context__.value(),
initial_pillar=initial_pillar,
) as st_:
if not _check_pillar(kwargs, st_.opts["pillar"]):
__context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE
err = ["Pillar failed to render with the following messages:"]
err += st_.opts["pillar"]["_errors"]
return err
try:
pillar = st_.opts["pillar"].value()
except AttributeError:
pillar = st_.opts["pillar"]
if pillar_override is not None or initial_pillar is None:
# Ensure other wrappers use the correct pillar
__pillar__.update(pillar)
st_.push_active()
chunks = st_.compile_low_chunks(context=__context__.value())
file_refs = salt.client.ssh.state.lowstate_file_refs(
@ -717,7 +740,7 @@ def highstate(test=None, **kwargs):
__context__["fileclient"],
chunks,
file_refs,
__pillar__.value(),
pillar,
st_kwargs["id_"],
roster_grains,
)
@ -764,26 +787,32 @@ def top(topfn, test=None, **kwargs):
salt '*' state.top reverse_top.sls exclude=sls_to_exclude
salt '*' state.top reverse_top.sls exclude="[{'id': 'id_to_exclude'}, {'sls': 'sls_to_exclude'}]"
"""
__pillar__.update(kwargs.get("pillar", {}))
st_kwargs = __salt__.kwargs
__opts__["grains"] = __grains__.value()
opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
if salt.utils.args.test_mode(test=test, **kwargs):
opts["test"] = True
else:
opts["test"] = __opts__.get("test", None)
opts["test"] = _get_test_value(test, **kwargs)
pillar_override = kwargs.get("pillar")
initial_pillar = _get_initial_pillar(opts)
with salt.client.ssh.state.SSHHighState(
opts,
__pillar__.value(),
pillar_override,
__salt__.value(),
__context__["fileclient"],
context=__context__.value(),
initial_pillar=initial_pillar,
) as st_:
if not _check_pillar(kwargs, st_.opts["pillar"]):
__context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE
err = ["Pillar failed to render with the following messages:"]
err += st_.opts["pillar"]["_errors"]
return err
try:
pillar = st_.opts["pillar"].value()
except AttributeError:
pillar = st_.opts["pillar"]
if pillar_override is not None or initial_pillar is None:
# Ensure other wrappers use the correct pillar
__pillar__.update(pillar)
st_.opts["state_top"] = os.path.join("salt://", topfn)
st_.push_active()
chunks = st_.compile_low_chunks(context=__context__.value())
@ -808,7 +837,7 @@ def top(topfn, test=None, **kwargs):
__context__["fileclient"],
chunks,
file_refs,
__pillar__.value(),
pillar,
st_kwargs["id_"],
roster_grains,
)
@ -855,18 +884,28 @@ def show_highstate(**kwargs):
"""
__opts__["grains"] = __grains__.value()
opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
pillar_override = kwargs.get("pillar")
initial_pillar = _get_initial_pillar(opts)
with salt.client.ssh.state.SSHHighState(
opts,
__pillar__.value(),
pillar_override,
__salt__,
__context__["fileclient"],
context=__context__.value(),
initial_pillar=initial_pillar,
) as st_:
if not _check_pillar(kwargs, st_.opts["pillar"]):
__context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE
err = ["Pillar failed to render with the following messages:"]
err += st_.opts["pillar"]["_errors"]
return err
try:
pillar = st_.opts["pillar"].value()
except AttributeError:
pillar = st_.opts["pillar"]
if pillar_override is not None or initial_pillar is None:
# Ensure other wrappers use the correct pillar
__pillar__.update(pillar)
st_.push_active()
chunks = st_.compile_highstate(context=__context__.value())
# Check for errors
@ -891,10 +930,11 @@ def show_lowstate(**kwargs):
opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
with salt.client.ssh.state.SSHHighState(
opts,
__pillar__.value(),
None,
__salt__,
__context__["fileclient"],
context=__context__.value(),
initial_pillar=_get_initial_pillar(opts),
) as st_:
if not _check_pillar(kwargs, st_.opts["pillar"]):
__context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE
@ -939,7 +979,6 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs):
salt '*' state.sls_id my_state my_module,a_common_module
"""
__pillar__.update(kwargs.get("pillar", {}))
st_kwargs = __salt__.kwargs
conflict = _check_queue(queue, kwargs)
if conflict is not None:
@ -953,12 +992,15 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs):
if opts["saltenv"] is None:
opts["saltenv"] = "base"
pillar_override = kwargs.get("pillar")
initial_pillar = _get_initial_pillar(opts)
with salt.client.ssh.state.SSHHighState(
__opts__,
__pillar__.value(),
pillar_override,
__salt__,
__context__["fileclient"],
context=__context__.value(),
initial_pillar=initial_pillar,
) as st_:
if not _check_pillar(kwargs, st_.opts["pillar"]):
@ -967,6 +1009,13 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs):
err += __pillar__["_errors"]
return err
try:
pillar = st_.opts["pillar"].value()
except AttributeError:
pillar = st_.opts["pillar"]
if pillar_override is not None or initial_pillar is None:
# Ensure other wrappers use the correct pillar
__pillar__.update(pillar)
split_mods = _parse_mods(mods)
st_.push_active()
high_, errors = st_.render_highstate(
@ -992,7 +1041,7 @@ def sls_id(id_, mods, test=None, queue=False, **kwargs):
)
)
ret = _ssh_state(chunk, st_kwargs, kwargs, test=test)
ret = _ssh_state(chunk, st_kwargs, kwargs, pillar, test=test)
_set_retcode(ret, highstate=highstate)
# Work around Windows multiprocessing bug, set __opts__['test'] back to
# value from before this function was run.
@ -1011,25 +1060,31 @@ def show_sls(mods, saltenv="base", test=None, **kwargs):
salt '*' state.show_sls core,edit.vim dev
"""
__pillar__.update(kwargs.get("pillar", {}))
__opts__["grains"] = __grains__.value()
opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
if salt.utils.args.test_mode(test=test, **kwargs):
opts["test"] = True
else:
opts["test"] = __opts__.get("test", None)
opts["test"] = _get_test_value(test, **kwargs)
pillar_override = kwargs.get("pillar")
initial_pillar = _get_initial_pillar(opts)
with salt.client.ssh.state.SSHHighState(
opts,
__pillar__.value(),
pillar_override,
__salt__,
__context__["fileclient"],
context=__context__.value(),
initial_pillar=initial_pillar,
) as st_:
if not _check_pillar(kwargs, st_.opts["pillar"]):
__context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE
err = ["Pillar failed to render with the following messages:"]
err += st_.opts["pillar"]["_errors"]
return err
try:
pillar = st_.opts["pillar"].value()
except AttributeError:
pillar = st_.opts["pillar"]
if pillar_override is not None or initial_pillar is None:
# Ensure other wrappers use the correct pillar
__pillar__.update(pillar)
st_.push_active()
mods = _parse_mods(mods)
high_data, errors = st_.render_highstate(
@ -1065,26 +1120,31 @@ def show_low_sls(mods, saltenv="base", test=None, **kwargs):
salt '*' state.show_low_sls core,edit.vim dev
"""
__pillar__.update(kwargs.get("pillar", {}))
__opts__["grains"] = __grains__.value()
opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
if salt.utils.args.test_mode(test=test, **kwargs):
opts["test"] = True
else:
opts["test"] = __opts__.get("test", None)
opts["test"] = _get_test_value(test, **kwargs)
pillar_override = kwargs.get("pillar")
initial_pillar = _get_initial_pillar(opts)
with salt.client.ssh.state.SSHHighState(
opts,
__pillar__.value(),
pillar_override,
__salt__,
__context__["fileclient"],
context=__context__.value(),
initial_pillar=initial_pillar,
) as st_:
if not _check_pillar(kwargs, st_.opts["pillar"]):
__context__["retcode"] = salt.defaults.exitcodes.EX_PILLAR_FAILURE
err = ["Pillar failed to render with the following messages:"]
err += st_.opts["pillar"]["_errors"]
return err
try:
pillar = st_.opts["pillar"].value()
except AttributeError:
pillar = st_.opts["pillar"]
if pillar_override is not None or initial_pillar is None:
# Ensure other wrappers use the correct pillar
__pillar__.update(pillar)
st_.push_active()
mods = _parse_mods(mods)
high_data, errors = st_.render_highstate(
@ -1122,10 +1182,11 @@ def show_top(**kwargs):
opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
with salt.client.ssh.state.SSHHighState(
opts,
__pillar__.value(),
None,
__salt__,
__context__["fileclient"],
context=__context__.value(),
initial_pillar=_get_initial_pillar(opts),
) as st_:
top_data = st_.get_top(context=__context__.value())
errors = []
@ -1171,17 +1232,22 @@ def single(fun, name, test=None, **kwargs):
opts = salt.utils.state.get_sls_opts(__opts__, **kwargs)
# Set test mode
if salt.utils.args.test_mode(test=test, **kwargs):
opts["test"] = True
else:
opts["test"] = __opts__.get("test", None)
opts["test"] = _get_test_value(test, **kwargs)
# Get the override pillar data
__pillar__.update(kwargs.get("pillar", {}))
# This needs to be removed from the kwargs, they are called
# as a lowstate with one item, not a single chunk
pillar_override = kwargs.pop("pillar", None)
# Create the State environment
st_ = salt.client.ssh.state.SSHState(opts, __pillar__)
st_ = salt.client.ssh.state.SSHState(
opts, pillar_override, initial_pillar=_get_initial_pillar(opts)
)
try:
pillar = st_.opts["pillar"].value()
except AttributeError:
pillar = st_.opts["pillar"]
# Verify the low chunk
err = st_.verify_data(kwargs)
if err:
@ -1208,7 +1274,7 @@ def single(fun, name, test=None, **kwargs):
__context__["fileclient"],
chunks,
file_refs,
__pillar__.value(),
pillar,
st_kwargs["id_"],
roster_grains,
)

View file

@ -10,6 +10,7 @@ System module for sleeping, restarting, and shutting down the system on Mac OS X
import getpass
import shlex
import salt.utils.mac_utils
import salt.utils.platform
from salt.exceptions import CommandExecutionError, SaltInvocationError
@ -71,7 +72,7 @@ def _execute_command(cmd, at_time=None):
Returns: bool
"""
if at_time:
cmd = "echo '{}' | at {}".format(cmd, shlex.quote(at_time))
cmd = f"echo '{cmd}' | at {shlex.quote(at_time)}"
return not bool(__salt__["cmd.retcode"](cmd, python_shell=True))
@ -204,10 +205,10 @@ def get_remote_login():
salt '*' system.get_remote_login
"""
ret = __utils__["mac_utils.execute_return_result"]("systemsetup -getremotelogin")
ret = salt.utils.mac_utils.execute_return_result("systemsetup -getremotelogin")
enabled = __utils__["mac_utils.validate_enabled"](
__utils__["mac_utils.parse_return"](ret)
enabled = salt.utils.mac_utils.validate_enabled(
salt.utils.mac_utils.parse_return(ret)
)
return enabled == "on"
@ -230,12 +231,12 @@ def set_remote_login(enable):
salt '*' system.set_remote_login True
"""
state = __utils__["mac_utils.validate_enabled"](enable)
state = salt.utils.mac_utils.validate_enabled(enable)
cmd = "systemsetup -f -setremotelogin {}".format(state)
__utils__["mac_utils.execute_return_success"](cmd)
cmd = f"systemsetup -f -setremotelogin {state}"
salt.utils.mac_utils.execute_return_success(cmd)
return __utils__["mac_utils.confirm_updated"](
return salt.utils.mac_utils.confirm_updated(
state, get_remote_login, normalize_ret=True
)
@ -253,12 +254,12 @@ def get_remote_events():
salt '*' system.get_remote_events
"""
ret = __utils__["mac_utils.execute_return_result"](
ret = salt.utils.mac_utils.execute_return_result(
"systemsetup -getremoteappleevents"
)
enabled = __utils__["mac_utils.validate_enabled"](
__utils__["mac_utils.parse_return"](ret)
enabled = salt.utils.mac_utils.validate_enabled(
salt.utils.mac_utils.parse_return(ret)
)
return enabled == "on"
@ -282,12 +283,12 @@ def set_remote_events(enable):
salt '*' system.set_remote_events On
"""
state = __utils__["mac_utils.validate_enabled"](enable)
state = salt.utils.mac_utils.validate_enabled(enable)
cmd = "systemsetup -setremoteappleevents {}".format(state)
__utils__["mac_utils.execute_return_success"](cmd)
cmd = f"systemsetup -setremoteappleevents {state}"
salt.utils.mac_utils.execute_return_success(cmd)
return __utils__["mac_utils.confirm_updated"](
return salt.utils.mac_utils.confirm_updated(
state,
get_remote_events,
normalize_ret=True,
@ -307,9 +308,9 @@ def get_computer_name():
salt '*' system.get_computer_name
"""
ret = __utils__["mac_utils.execute_return_result"]("scutil --get ComputerName")
ret = salt.utils.mac_utils.execute_return_result("scutil --get ComputerName")
return __utils__["mac_utils.parse_return"](ret)
return salt.utils.mac_utils.parse_return(ret)
def set_computer_name(name):
@ -327,10 +328,10 @@ def set_computer_name(name):
salt '*' system.set_computer_name "Mike's Mac"
"""
cmd = 'scutil --set ComputerName "{}"'.format(name)
__utils__["mac_utils.execute_return_success"](cmd)
cmd = f'scutil --set ComputerName "{name}"'
salt.utils.mac_utils.execute_return_success(cmd)
return __utils__["mac_utils.confirm_updated"](
return salt.utils.mac_utils.confirm_updated(
name,
get_computer_name,
)
@ -349,11 +350,9 @@ def get_subnet_name():
salt '*' system.get_subnet_name
"""
ret = __utils__["mac_utils.execute_return_result"](
"systemsetup -getlocalsubnetname"
)
ret = salt.utils.mac_utils.execute_return_result("systemsetup -getlocalsubnetname")
return __utils__["mac_utils.parse_return"](ret)
return salt.utils.mac_utils.parse_return(ret)
def set_subnet_name(name):
@ -375,10 +374,10 @@ def set_subnet_name(name):
The following will be set as 'Mikes-Mac'
salt '*' system.set_subnet_name "Mike's Mac"
"""
cmd = 'systemsetup -setlocalsubnetname "{}"'.format(name)
__utils__["mac_utils.execute_return_success"](cmd)
cmd = f'systemsetup -setlocalsubnetname "{name}"'
salt.utils.mac_utils.execute_return_success(cmd)
return __utils__["mac_utils.confirm_updated"](
return salt.utils.mac_utils.confirm_updated(
name,
get_subnet_name,
)
@ -397,9 +396,9 @@ def get_startup_disk():
salt '*' system.get_startup_disk
"""
ret = __utils__["mac_utils.execute_return_result"]("systemsetup -getstartupdisk")
ret = salt.utils.mac_utils.execute_return_result("systemsetup -getstartupdisk")
return __utils__["mac_utils.parse_return"](ret)
return salt.utils.mac_utils.parse_return(ret)
def list_startup_disks():
@ -415,7 +414,7 @@ def list_startup_disks():
salt '*' system.list_startup_disks
"""
ret = __utils__["mac_utils.execute_return_result"]("systemsetup -liststartupdisks")
ret = salt.utils.mac_utils.execute_return_result("systemsetup -liststartupdisks")
return ret.splitlines()
@ -445,10 +444,10 @@ def set_startup_disk(path):
)
raise SaltInvocationError(msg)
cmd = "systemsetup -setstartupdisk {}".format(path)
__utils__["mac_utils.execute_return_result"](cmd)
cmd = f"systemsetup -setstartupdisk {path}"
salt.utils.mac_utils.execute_return_result(cmd)
return __utils__["mac_utils.confirm_updated"](
return salt.utils.mac_utils.confirm_updated(
path,
get_startup_disk,
)
@ -469,11 +468,11 @@ def get_restart_delay():
salt '*' system.get_restart_delay
"""
ret = __utils__["mac_utils.execute_return_result"](
ret = salt.utils.mac_utils.execute_return_result(
"systemsetup -getwaitforstartupafterpowerfailure"
)
return __utils__["mac_utils.parse_return"](ret)
return salt.utils.mac_utils.parse_return(ret)
def set_restart_delay(seconds):
@ -512,10 +511,10 @@ def set_restart_delay(seconds):
)
raise SaltInvocationError(msg)
cmd = "systemsetup -setwaitforstartupafterpowerfailure {}".format(seconds)
__utils__["mac_utils.execute_return_success"](cmd)
cmd = f"systemsetup -setwaitforstartupafterpowerfailure {seconds}"
salt.utils.mac_utils.execute_return_success(cmd)
return __utils__["mac_utils.confirm_updated"](
return salt.utils.mac_utils.confirm_updated(
seconds,
get_restart_delay,
)
@ -535,12 +534,12 @@ def get_disable_keyboard_on_lock():
salt '*' system.get_disable_keyboard_on_lock
"""
ret = __utils__["mac_utils.execute_return_result"](
ret = salt.utils.mac_utils.execute_return_result(
"systemsetup -getdisablekeyboardwhenenclosurelockisengaged"
)
enabled = __utils__["mac_utils.validate_enabled"](
__utils__["mac_utils.parse_return"](ret)
enabled = salt.utils.mac_utils.validate_enabled(
salt.utils.mac_utils.parse_return(ret)
)
return enabled == "on"
@ -564,12 +563,12 @@ def set_disable_keyboard_on_lock(enable):
salt '*' system.set_disable_keyboard_on_lock False
"""
state = __utils__["mac_utils.validate_enabled"](enable)
state = salt.utils.mac_utils.validate_enabled(enable)
cmd = "systemsetup -setdisablekeyboardwhenenclosurelockisengaged {}".format(state)
__utils__["mac_utils.execute_return_success"](cmd)
cmd = f"systemsetup -setdisablekeyboardwhenenclosurelockisengaged {state}"
salt.utils.mac_utils.execute_return_success(cmd)
return __utils__["mac_utils.confirm_updated"](
return salt.utils.mac_utils.confirm_updated(
state,
get_disable_keyboard_on_lock,
normalize_ret=True,
@ -589,11 +588,11 @@ def get_boot_arch():
salt '*' system.get_boot_arch
"""
ret = __utils__["mac_utils.execute_return_result"](
ret = salt.utils.mac_utils.execute_return_result(
"systemsetup -getkernelbootarchitecturesetting"
)
arch = __utils__["mac_utils.parse_return"](ret)
arch = salt.utils.mac_utils.parse_return(ret)
if "default" in arch:
return "default"
@ -639,10 +638,10 @@ def set_boot_arch(arch="default"):
)
raise SaltInvocationError(msg)
cmd = "systemsetup -setkernelbootarchitecture {}".format(arch)
__utils__["mac_utils.execute_return_success"](cmd)
cmd = f"systemsetup -setkernelbootarchitecture {arch}"
salt.utils.mac_utils.execute_return_success(cmd)
return __utils__["mac_utils.confirm_updated"](
return salt.utils.mac_utils.confirm_updated(
arch,
get_boot_arch,
)

View file

@ -1,263 +0,0 @@
"""
integration tests for mac_system
"""
import logging
import pytest
from saltfactories.utils import random_string
from tests.support.case import ModuleCase
log = logging.getLogger(__name__)
SET_COMPUTER_NAME = random_string("RS-", lowercase=False)
SET_SUBNET_NAME = random_string("RS-", lowercase=False)
@pytest.mark.flaky(max_runs=10)
@pytest.mark.skip_unless_on_darwin
@pytest.mark.usefixtures("salt_sub_minion")
@pytest.mark.skip_if_not_root
@pytest.mark.skip_if_binaries_missing("systemsetup")
@pytest.mark.slow_test
class MacSystemModuleTest(ModuleCase):
"""
Validate the mac_system module
"""
ATRUN_ENABLED = False
REMOTE_LOGIN_ENABLED = False
REMOTE_EVENTS_ENABLED = False
SUBNET_NAME = ""
KEYBOARD_DISABLED = False
def setUp(self):
"""
Get current settings
"""
self.ATRUN_ENABLED = self.run_function("service.enabled", ["com.apple.atrun"])
self.REMOTE_LOGIN_ENABLED = self.run_function("system.get_remote_login")
self.REMOTE_EVENTS_ENABLED = self.run_function("system.get_remote_events")
self.SUBNET_NAME = self.run_function("system.get_subnet_name")
self.KEYBOARD_DISABLED = self.run_function(
"system.get_disable_keyboard_on_lock"
)
def tearDown(self):
"""
Reset to original settings
"""
if not self.ATRUN_ENABLED:
atrun = "/System/Library/LaunchDaemons/com.apple.atrun.plist"
self.run_function("service.stop", [atrun])
self.run_function("system.set_remote_login", [self.REMOTE_LOGIN_ENABLED])
self.run_function("system.set_remote_events", [self.REMOTE_EVENTS_ENABLED])
self.run_function("system.set_subnet_name", [self.SUBNET_NAME])
self.run_function(
"system.set_disable_keyboard_on_lock", [self.KEYBOARD_DISABLED]
)
@pytest.mark.destructive_test
@pytest.mark.slow_test
def test_get_set_remote_login(self):
"""
Test system.get_remote_login
Test system.set_remote_login
"""
# Normal Functionality
self.assertTrue(self.run_function("system.set_remote_login", [True]))
self.assertTrue(self.run_function("system.get_remote_login"))
self.assertTrue(self.run_function("system.set_remote_login", [False]))
self.assertFalse(self.run_function("system.get_remote_login"))
# Test valid input
self.assertTrue(self.run_function("system.set_remote_login", [True]))
self.assertTrue(self.run_function("system.set_remote_login", [False]))
self.assertTrue(self.run_function("system.set_remote_login", ["yes"]))
self.assertTrue(self.run_function("system.set_remote_login", ["no"]))
self.assertTrue(self.run_function("system.set_remote_login", ["On"]))
self.assertTrue(self.run_function("system.set_remote_login", ["Off"]))
self.assertTrue(self.run_function("system.set_remote_login", [1]))
self.assertTrue(self.run_function("system.set_remote_login", [0]))
# Test invalid input
self.assertIn(
"Invalid String Value for Enabled",
self.run_function("system.set_remote_login", ["spongebob"]),
)
@pytest.mark.destructive_test
@pytest.mark.slow_test
def test_get_set_remote_events(self):
"""
Test system.get_remote_events
Test system.set_remote_events
"""
# Normal Functionality
self.assertTrue(self.run_function("system.set_remote_events", [True]))
self.assertTrue(self.run_function("system.get_remote_events"))
self.assertTrue(self.run_function("system.set_remote_events", [False]))
self.assertFalse(self.run_function("system.get_remote_events"))
# Test valid input
self.assertTrue(self.run_function("system.set_remote_events", [True]))
self.assertTrue(self.run_function("system.set_remote_events", [False]))
self.assertTrue(self.run_function("system.set_remote_events", ["yes"]))
self.assertTrue(self.run_function("system.set_remote_events", ["no"]))
self.assertTrue(self.run_function("system.set_remote_events", ["On"]))
self.assertTrue(self.run_function("system.set_remote_events", ["Off"]))
self.assertTrue(self.run_function("system.set_remote_events", [1]))
self.assertTrue(self.run_function("system.set_remote_events", [0]))
# Test invalid input
self.assertIn(
"Invalid String Value for Enabled",
self.run_function("system.set_remote_events", ["spongebob"]),
)
@pytest.mark.destructive_test
@pytest.mark.slow_test
def test_get_set_subnet_name(self):
"""
Test system.get_subnet_name
Test system.set_subnet_name
"""
self.assertTrue(self.run_function("system.set_subnet_name", [SET_SUBNET_NAME]))
self.assertEqual(self.run_function("system.get_subnet_name"), SET_SUBNET_NAME)
@pytest.mark.slow_test
@pytest.mark.skip_initial_gh_actions_failure
def test_get_list_startup_disk(self):
"""
Test system.get_startup_disk
Test system.list_startup_disks
Don't know how to test system.set_startup_disk as there's usually only
one startup disk available on a system
"""
# Test list and get
ret = self.run_function("system.list_startup_disks")
self.assertIsInstance(ret, list)
self.assertIn(self.run_function("system.get_startup_disk"), ret)
# Test passing set a bad disk
self.assertIn(
"Invalid value passed for path.",
self.run_function("system.set_startup_disk", ["spongebob"]),
)
@pytest.mark.skip(reason="Skip this test until mac fixes it.")
def test_get_set_restart_delay(self):
"""
Test system.get_restart_delay
Test system.set_restart_delay
system.set_restart_delay does not work due to an apple bug, see docs
may need to disable this test as we can't control the delay value
"""
# Normal Functionality
self.assertTrue(self.run_function("system.set_restart_delay", [90]))
self.assertEqual(self.run_function("system.get_restart_delay"), "90 seconds")
# Pass set bad value for seconds
self.assertIn(
"Invalid value passed for seconds.",
self.run_function("system.set_restart_delay", [70]),
)
@pytest.mark.slow_test
def test_get_set_disable_keyboard_on_lock(self):
"""
Test system.get_disable_keyboard_on_lock
Test system.set_disable_keyboard_on_lock
"""
# Normal Functionality
self.assertTrue(
self.run_function("system.set_disable_keyboard_on_lock", [True])
)
self.assertTrue(self.run_function("system.get_disable_keyboard_on_lock"))
self.assertTrue(
self.run_function("system.set_disable_keyboard_on_lock", [False])
)
self.assertFalse(self.run_function("system.get_disable_keyboard_on_lock"))
# Test valid input
self.assertTrue(
self.run_function("system.set_disable_keyboard_on_lock", [True])
)
self.assertTrue(
self.run_function("system.set_disable_keyboard_on_lock", [False])
)
self.assertTrue(
self.run_function("system.set_disable_keyboard_on_lock", ["yes"])
)
self.assertTrue(
self.run_function("system.set_disable_keyboard_on_lock", ["no"])
)
self.assertTrue(
self.run_function("system.set_disable_keyboard_on_lock", ["On"])
)
self.assertTrue(
self.run_function("system.set_disable_keyboard_on_lock", ["Off"])
)
self.assertTrue(self.run_function("system.set_disable_keyboard_on_lock", [1]))
self.assertTrue(self.run_function("system.set_disable_keyboard_on_lock", [0]))
# Test invalid input
self.assertIn(
"Invalid String Value for Enabled",
self.run_function("system.set_disable_keyboard_on_lock", ["spongebob"]),
)
@pytest.mark.skip(reason="Skip this test until mac fixes it.")
def test_get_set_boot_arch(self):
"""
Test system.get_boot_arch
Test system.set_boot_arch
system.set_boot_arch does not work due to an apple bug, see docs
may need to disable this test as we can't set the boot architecture
"""
# Normal Functionality
self.assertTrue(self.run_function("system.set_boot_arch", ["i386"]))
self.assertEqual(self.run_function("system.get_boot_arch"), "i386")
self.assertTrue(self.run_function("system.set_boot_arch", ["default"]))
self.assertEqual(self.run_function("system.get_boot_arch"), "default")
# Test invalid input
self.assertIn(
"Invalid value passed for arch",
self.run_function("system.set_boot_arch", ["spongebob"]),
)
@pytest.mark.skip_unless_on_darwin
@pytest.mark.skip_if_not_root
class MacSystemComputerNameTest(ModuleCase):
def setUp(self):
self.COMPUTER_NAME = self.run_function("system.get_computer_name")
self.wait_for_all_jobs()
def tearDown(self):
self.run_function("system.set_computer_name", [self.COMPUTER_NAME])
self.wait_for_all_jobs()
# A similar test used to be skipped on py3 due to 'hanging', if we see
# something similar again we may want to skip this gain until we
# investigate
# @pytest.mark.skipif(salt.utils.platform.is_darwin() and six.PY3, reason='This test hangs on OS X on Py3. Skipping until #53566 is merged.')
@pytest.mark.destructive_test
@pytest.mark.slow_test
def test_get_set_computer_name(self):
"""
Test system.get_computer_name
Test system.set_computer_name
"""
log.debug("Set name is %s", SET_COMPUTER_NAME)
self.assertTrue(
self.run_function("system.set_computer_name", [SET_COMPUTER_NAME])
)
self.assertEqual(
self.run_function("system.get_computer_name"), SET_COMPUTER_NAME
)

View file

@ -0,0 +1,356 @@
"""
integration tests for mac_system
"""
import logging
import pytest
from saltfactories.utils import random_string
from salt.exceptions import CommandExecutionError, SaltInvocationError
log = logging.getLogger(__name__)
pytestmark = [
pytest.mark.slow_test,
pytest.mark.skip_if_not_root,
pytest.mark.skip_unless_on_darwin,
pytest.mark.skip_if_binaries_missing("systemsetup"),
]
@pytest.fixture
def service(modules):
return modules.service
@pytest.fixture
def system(modules):
return modules.system
@pytest.fixture
def _remote_login_cleanup(system, grains):
if grains["osmajorrelease"] >= 13:
pytest.skip("SKipping until we figure out how to have full dist access")
remote_login_enabled = system.get_remote_login()
try:
yield
finally:
if system.get_remote_login() != remote_login_enabled:
system.set_remote_login(remote_login_enabled)
@pytest.fixture
def _remote_events_cleanup(system, grains):
if grains["osmajorrelease"] >= 13:
pytest.skip("SKipping until we figure out how to have full dist access")
remote_events_enabled = system.get_remote_events()
try:
yield
finally:
if system.get_remote_events() != remote_events_enabled:
system.set_remote_events(remote_events_enabled)
@pytest.fixture
def _subnet_cleanup(system):
subnet_name = system.get_subnet_name()
try:
yield
finally:
if system.get_subnet_name() != subnet_name:
system.set_subnet_name(subnet_name)
@pytest.fixture
def _keyboard_cleanup(system):
keyboard_disabled = system.get_disable_keyboard_on_lock()
try:
yield
finally:
if system.get_disable_keyboard_on_lock() != keyboard_disabled:
system.set_disable_keyboard_on_lock(keyboard_disabled)
@pytest.fixture
def _computer_name_cleanup(system):
computer_name = system.get_computer_name()
try:
yield
finally:
if system.get_computer_name() != computer_name:
system.set_computer_name(computer_name)
@pytest.fixture(autouse=True)
def _setup_teardown_vars(service, system):
atrun_enabled = service.enabled("com.apple.atrun")
try:
yield
finally:
if not atrun_enabled:
atrun = "/System/Library/LaunchDaemons/com.apple.atrun.plist"
service.stop(atrun)
@pytest.mark.usefixtures("_remote_login_cleanup")
def test_get_set_remote_login(system):
"""
Test system.get_remote_login
Test system.set_remote_login
"""
# Normal Functionality
ret = system.set_remote_login(True)
assert ret
ret = system.get_remote_login()
assert ret
ret = system.set_remote_login(False)
assert ret
ret = system.get_remote_login()
assert not ret
# Test valid input
ret = system.set_remote_login(True)
assert ret
ret = system.set_remote_login(False)
assert ret
ret = system.set_remote_login("yes")
assert ret
ret = system.set_remote_login("no")
assert ret
ret = system.set_remote_login("On")
assert ret
ret = system.set_remote_login("Off")
assert ret
ret = system.set_remote_login(1)
assert ret
ret = system.set_remote_login(0)
assert ret
# Test invalid input
with pytest.raises(SaltInvocationError) as exc:
system.set_remote_login("spongebob")
assert "Invalid String Value for Enabled" in str(exc.value)
@pytest.mark.skip_initial_gh_actions_failure
@pytest.mark.usefixtures("_remote_events_cleanup")
def test_get_set_remote_events(system):
"""
Test system.get_remote_events
Test system.set_remote_events
"""
# Normal Functionality
ret = system.set_remote_events(True)
assert ret
ret = system.get_remote_events()
assert ret
ret = system.set_remote_events(False)
assert ret
ret = not system.get_remote_events()
assert not ret
# Test valid input
ret = system.set_remote_events(True)
assert ret
ret = system.set_remote_events(False)
assert ret
ret = system.set_remote_events("yes")
assert ret
ret = system.set_remote_events("no")
assert ret
ret = system.set_remote_events("On")
assert ret
ret = system.set_remote_events("Off")
assert ret
ret = system.set_remote_events(1)
assert ret
ret = system.set_remote_events(0)
assert ret
# Test invalid input
with pytest.raises(CommandExecutionError) as exc:
system.set_remote_events("spongebob")
assert "Invalid String Value for Enabled" in str(exc.value)
@pytest.mark.usefixtures("_subnet_cleanup")
def test_get_set_subnet_name(system):
"""
Test system.get_subnet_name
Test system.set_subnet_name
"""
set_subnet_name = random_string("RS-", lowercase=False)
ret = system.set_subnet_name(set_subnet_name)
assert ret
ret = system.get_subnet_name()
assert ret == set_subnet_name
@pytest.mark.skip_initial_gh_actions_failure
def test_get_list_startup_disk(system):
"""
Test system.get_startup_disk
Test system.list_startup_disks
Don't know how to test system.set_startup_disk as there's usually only
one startup disk available on a system
"""
# Test list and get
ret = system.list_startup_disks()
assert isinstance(ret, list)
startup_disk = system.get_startup_disk()
assert startup_disk in ret
# Test passing set a bad disk
with pytest.raises(SaltInvocationError) as exc:
system.set_startup_disk("spongebob")
assert "Invalid value passed for path." in str(exc.value)
@pytest.mark.skip(reason="Skip this test until mac fixes it.")
def test_get_set_restart_delay(system):
"""
Test system.get_restart_delay
Test system.set_restart_delay
system.set_restart_delay does not work due to an apple bug, see docs
may need to disable this test as we can't control the delay value
"""
# Normal Functionality
ret = system.set_restart_delay(90)
assert ret
ret = system.get_restart_delay()
assert ret == "90 seconds"
# Pass set bad value for seconds
with pytest.raises(CommandExecutionError) as exc:
system.set_restart_delay(70)
assert "Invalid value passed for seconds." in str(exc.value)
@pytest.mark.usefixtures("_keyboard_cleanup")
def test_get_set_disable_keyboard_on_lock(system):
"""
Test system.get_disable_keyboard_on_lock
Test system.set_disable_keyboard_on_lock
"""
# Normal Functionality
ret = system.set_disable_keyboard_on_lock(True)
assert ret
ret = system.get_disable_keyboard_on_lock()
assert ret
ret = system.set_disable_keyboard_on_lock(False)
assert ret
ret = system.get_disable_keyboard_on_lock()
assert not ret
# Test valid input
ret = system.set_disable_keyboard_on_lock(True)
assert ret
ret = system.set_disable_keyboard_on_lock(False)
assert ret
ret = system.set_disable_keyboard_on_lock("yes")
assert ret
ret = system.set_disable_keyboard_on_lock("no")
assert ret
ret = system.set_disable_keyboard_on_lock("On")
assert ret
ret = system.set_disable_keyboard_on_lock("Off")
assert ret
ret = system.set_disable_keyboard_on_lock(1)
assert ret
ret = system.set_disable_keyboard_on_lock(0)
assert ret
# Test invalid input
with pytest.raises(SaltInvocationError) as exc:
system.set_disable_keyboard_on_lock("spongebob")
assert "Invalid String Value for Enabled" in str(exc.value)
@pytest.mark.skip(reason="Skip this test until mac fixes it.")
def test_get_set_boot_arch(system):
"""
Test system.get_boot_arch
Test system.set_boot_arch
system.set_boot_arch does not work due to an apple bug, see docs
may need to disable this test as we can't set the boot architecture
"""
# Normal Functionality
ret = system.set_boot_arch("i386")
assert ret
ret = system.get_boot_arch()
assert ret == "i386"
ret = system.set_boot_arch("default")
assert ret
ret = system.get_boot_arch()
assert ret == "default"
# Test invalid input
with pytest.raises(CommandExecutionError) as exc:
system.set_boot_arch("spongebob")
assert "Invalid value passed for arch" in str(exc.value)
# A similar test used to be skipped on py3 due to 'hanging', if we see
# something similar again we may want to skip this gain until we
# investigate
# @pytest.mark.skipif(salt.utils.platform.is_darwin() and six.PY3, reason='This test hangs on OS X on Py3. Skipping until #53566 is merged.')
@pytest.mark.destructive_test
@pytest.mark.usefixtures("_computer_name_cleanup")
def test_get_set_computer_name(system):
"""
Test system.get_computer_name
Test system.set_computer_name
"""
set_computer_name = random_string("RS-", lowercase=False)
computer_name = system.get_computer_name()
log.debug("set name is %s", set_computer_name)
ret = system.set_computer_name(set_computer_name)
assert ret
ret = system.get_computer_name()
assert ret == set_computer_name
system.set_computer_name(computer_name)

View file

@ -70,7 +70,22 @@ def PKG_CAP_TARGETS(grains):
_PKG_CAP_TARGETS = []
if grains["os_family"] == "Suse":
if grains["os"] == "SUSE":
_PKG_CAP_TARGETS = [("perl(ZNC)", "znc-perl")]
_PKG_CAP_TARGETS = [("perl(YAML)", "perl-YAML")]
# sudo zypper install 'perl(YAML)'
# Loading repository data...
# Reading installed packages...
# 'perl(YAML)' not found in package names. Trying capabilities.
# Resolving package dependencies...
#
# The following NEW package is going to be installed:
# perl-YAML
#
# 1 new package to install.
# Overall download size: 85.3 KiB. Already cached: 0 B. After the operation, additional 183.3 KiB will be used.
# Continue? [y/n/v/...? shows all options] (y):
# So, it just doesn't work here? skip it for now
_PKG_CAP_TARGETS.clear()
if not _PKG_CAP_TARGETS:
pytest.skip("Capability not provided")
return _PKG_CAP_TARGETS

View file

@ -417,11 +417,13 @@ class TestVirtMigrateTest:
assert domains == []
def test_ssh_migration(
self, salt_cli, virt_minion_0, virt_minion_1, prep_virt, virt_domain
self, salt_cli, virt_minion_0, virt_minion_1, prep_virt, virt_domain, grains
):
"""
Test domain migration over SSH, TCP and TLS transport protocol
"""
if grains["os"] == "VMware Photon OS" and grains["osmajorrelease"] == 3:
pytest.skip("Skipping this test on PhotonOS 3")
ret = salt_cli.run("virt.list_active_vms", minion_tgt=virt_minion_0.id)
assert ret.returncode == 0, ret

View file

@ -0,0 +1,132 @@
import pytest
@pytest.fixture(scope="module")
def state_tree(base_env_state_tree_root_dir):
top_file = """
{%- from "map.jinja" import abc with context %}
base:
'localhost':
- basic
'127.0.0.1':
- basic
"""
map_file = """
{%- set abc = "def" %}
"""
state_file = """
{%- from "map.jinja" import abc with context %}
Ok with {{ abc }}:
test.succeed_without_changes
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
map_tempfile = pytest.helpers.temp_file(
"map.jinja", map_file, base_env_state_tree_root_dir
)
state_tempfile = pytest.helpers.temp_file(
"test.sls", state_file, base_env_state_tree_root_dir
)
with top_tempfile, map_tempfile, state_tempfile:
yield
@pytest.fixture(scope="module")
def state_tree_dir(base_env_state_tree_root_dir):
"""
State tree with files to test salt-ssh
when the map.jinja file is in another directory
"""
top_file = """
{%- from "test/map.jinja" import abc with context %}
base:
'localhost':
- test
'127.0.0.1':
- test
"""
map_file = """
{%- set abc = "def" %}
"""
state_file = """
{%- from "test/map.jinja" import abc with context %}
Ok with {{ abc }}:
test.succeed_without_changes
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
map_tempfile = pytest.helpers.temp_file(
"test/map.jinja", map_file, base_env_state_tree_root_dir
)
state_tempfile = pytest.helpers.temp_file(
"test.sls", state_file, base_env_state_tree_root_dir
)
with top_tempfile, map_tempfile, state_tempfile:
yield
@pytest.fixture
def nested_state_tree(base_env_state_tree_root_dir, tmp_path):
top_file = """
base:
'localhost':
- basic
'127.0.0.1':
- basic
"""
state_file = """
/{}/file.txt:
file.managed:
- source: salt://foo/file.jinja
- template: jinja
""".format(
tmp_path
)
file_jinja = """
{% from 'foo/map.jinja' import comment %}{{ comment }}
"""
map_file = """
{% set comment = "blah blah" %}
"""
statedir = base_env_state_tree_root_dir / "foo"
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
map_tempfile = pytest.helpers.temp_file("map.jinja", map_file, statedir)
file_tempfile = pytest.helpers.temp_file("file.jinja", file_jinja, statedir)
state_tempfile = pytest.helpers.temp_file("init.sls", state_file, statedir)
with top_tempfile, map_tempfile, state_tempfile, file_tempfile:
yield
@pytest.fixture(scope="module")
def pillar_tree_nested(base_env_pillar_tree_root_dir):
top_file = """
base:
'localhost':
- nested
'127.0.0.1':
- nested
"""
nested_pillar = r"""
{%- do salt.log.warning("hithere: pillar was rendered") %}
monty: python
the_meaning:
of:
life: 42
bar: tender
for: what
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_pillar_tree_root_dir
)
nested_tempfile = pytest.helpers.temp_file(
"nested.sls", nested_pillar, base_env_pillar_tree_root_dir
)
with top_tempfile, nested_tempfile:
yield

View file

@ -0,0 +1,189 @@
"""
Ensure pillar overrides are merged recursively, that wrapper
modules are in sync with the pillar dict in the rendering environment
and that the pillars are available on the target.
"""
import json
import pytest
import salt.utils.dictupdate
pytestmark = [
pytest.mark.skip_on_windows(reason="salt-ssh not available on Windows"),
pytest.mark.usefixtures("pillar_tree_nested"),
pytest.mark.slow_test,
]
def test_pillar_is_only_rendered_once_without_overrides(salt_ssh_cli, caplog):
ret = salt_ssh_cli.run("state.apply", "test")
assert ret.returncode == 0
assert isinstance(ret.data, dict)
assert ret.data
assert ret.data[next(iter(ret.data))]["result"] is True
assert caplog.text.count("hithere: pillar was rendered") == 1
def test_pillar_is_rerendered_with_overrides(salt_ssh_cli, caplog):
ret = salt_ssh_cli.run("state.apply", "test", pillar={"foo": "bar"})
assert ret.returncode == 0
assert isinstance(ret.data, dict)
assert ret.data
assert ret.data[next(iter(ret.data))]["result"] is True
assert caplog.text.count("hithere: pillar was rendered") == 2
@pytest.fixture(scope="module", autouse=True)
def _show_pillar_state(base_env_state_tree_root_dir):
top_file = """
base:
'localhost':
- showpillar
'127.0.0.1':
- showpillar
"""
show_pillar_sls = """
deep_thought:
test.show_notification:
- text: '{{ {
"raw": {
"the_meaning": pillar.get("the_meaning"),
"btw": pillar.get("btw")},
"wrapped": {
"the_meaning": salt["pillar.get"]("the_meaning"),
"btw": salt["pillar.get"]("btw")}}
| json }}'
target_check:
test.check_pillar:
- present:
- the_meaning:of:foo
- btw
- the_meaning:of:bar
- the_meaning:for
- listing:
- the_meaning:of:life
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
show_tempfile = pytest.helpers.temp_file(
"showpillar.sls", show_pillar_sls, base_env_state_tree_root_dir
)
with top_tempfile, show_tempfile:
yield
@pytest.fixture
def base():
return {"the_meaning": {"of": {"life": 42, "bar": "tender"}, "for": "what"}}
@pytest.fixture
def override(base):
poverride = {
"the_meaning": {"of": {"life": [2.71], "foo": "lish"}},
"btw": "turtles",
}
expected = salt.utils.dictupdate.merge(base, poverride)
return expected, poverride
def test_state_sls(salt_ssh_cli, override):
expected, override = override
ret = salt_ssh_cli.run("state.sls", "showpillar", pillar=override)
_assert_basic(ret)
assert len(ret.data) == 2
for sid, sret in ret.data.items():
if "show" in sid:
_assert_pillar(sret["comment"], expected)
else:
assert sret["result"] is True
@pytest.mark.parametrize("sid", ("deep_thought", "target_check"))
def test_state_sls_id(salt_ssh_cli, sid, override):
expected, override = override
ret = salt_ssh_cli.run("state.sls_id", sid, "showpillar", pillar=override)
_assert_basic(ret)
state_res = ret.data[next(iter(ret.data))]
if sid == "deep_thought":
_assert_pillar(state_res["comment"], expected)
else:
assert state_res["result"] is True
def test_state_highstate(salt_ssh_cli, override):
expected, override = override
ret = salt_ssh_cli.run("state.highstate", pillar=override, whitelist=["showpillar"])
_assert_basic(ret)
assert len(ret.data) == 2
for sid, sret in ret.data.items():
if "show" in sid:
_assert_pillar(sret["comment"], expected)
else:
assert sret["result"] is True
def test_state_show_sls(salt_ssh_cli, override):
expected, override = override
ret = salt_ssh_cli.run("state.show_sls", "showpillar", pillar=override)
_assert_basic(ret)
pillar = ret.data["deep_thought"]["test"]
pillar = next(x["text"] for x in pillar if isinstance(x, dict))
_assert_pillar(pillar, expected)
def test_state_show_low_sls(salt_ssh_cli, override):
expected, override = override
ret = salt_ssh_cli.run("state.show_low_sls", "showpillar", pillar=override)
_assert_basic(ret, list)
pillar = ret.data[0]["text"]
_assert_pillar(pillar, expected)
def test_state_single(salt_ssh_cli, override):
expected, override = override
ret = salt_ssh_cli.run(
"state.single",
"test.check_pillar",
"foo",
present=[
"the_meaning:of:foo",
"btw",
"the_meaning:of:bar",
"the_meaning:for",
],
listing=["the_meaning:of:life"],
pillar=override,
)
_assert_basic(ret, dict)
state_res = ret.data[next(iter(ret.data))]
assert state_res["result"] is True
def test_state_top(salt_ssh_cli, override):
expected, override = override
ret = salt_ssh_cli.run("state.top", "top.sls", pillar=override)
_assert_basic(ret)
assert len(ret.data) == 2
for sid, sret in ret.data.items():
if "show" in sid:
_assert_pillar(sret["comment"], expected)
else:
assert sret["result"] is True
def _assert_pillar(pillar, expected):
if not isinstance(pillar, dict):
pillar = json.loads(pillar)
assert pillar["raw"] == expected
assert pillar["wrapped"] == expected
def _assert_basic(ret, typ=dict):
assert ret.returncode == 0
assert isinstance(ret.data, typ)
assert ret.data

View file

@ -0,0 +1,97 @@
"""
Specifically ensure that pillars are merged as expected
for the target as well and available for renderers.
This should be covered by `test.check_pillar` above, but
let's check the specific output for the most important funcs.
Issue #59802
"""
import json
import pytest
import salt.utils.dictupdate
pytestmark = [
pytest.mark.skip_on_windows(reason="salt-ssh not available on Windows"),
pytest.mark.usefixtures("pillar_tree_nested"),
pytest.mark.slow_test,
]
@pytest.fixture
def _write_pillar_state(base_env_state_tree_root_dir, tmp_path_factory):
tmp_path = tmp_path_factory.mktemp("tgtdir")
tgt_file = tmp_path / "deepthought.txt"
top_file = """
base:
'localhost':
- writepillar
'127.0.0.1':
- writepillar
"""
nested_pillar_file = f"""
deep_thought:
file.managed:
- name: {tgt_file}
- source: salt://deepthought.txt.jinja
- template: jinja
"""
deepthought = r"""
{{
{
"raw": {
"the_meaning": pillar.get("the_meaning"),
"btw": pillar.get("btw")},
"modules": {
"the_meaning": salt["pillar.get"]("the_meaning"),
"btw": salt["pillar.get"]("btw")}
} | json }}
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
show_tempfile = pytest.helpers.temp_file(
"writepillar.sls", nested_pillar_file, base_env_state_tree_root_dir
)
deepthought_tempfile = pytest.helpers.temp_file(
"deepthought.txt.jinja", deepthought, base_env_state_tree_root_dir
)
with top_tempfile, show_tempfile, deepthought_tempfile:
yield tgt_file
@pytest.fixture
def base():
return {"the_meaning": {"of": {"life": 42, "bar": "tender"}, "for": "what"}}
@pytest.fixture
def override(base):
poverride = {
"the_meaning": {"of": {"life": 2.71, "foo": "lish"}},
"btw": "turtles",
}
expected = salt.utils.dictupdate.merge(base, poverride)
return expected, poverride
@pytest.mark.parametrize(
"args,kwargs",
(
(("state.sls", "writepillar"), {}),
(("state.highstate",), {"whitelist": "writepillar"}),
(("state.top", "top.sls"), {}),
),
)
def test_it(salt_ssh_cli, args, kwargs, override, _write_pillar_state):
expected, override = override
ret = salt_ssh_cli.run(*args, **kwargs, pillar=override)
assert ret.returncode == 0
assert isinstance(ret.data, dict)
assert ret.data
assert _write_pillar_state.exists()
pillar = json.loads(_write_pillar_state.read_text())
assert pillar["raw"] == expected
assert pillar["modules"] == expected

View file

@ -0,0 +1,62 @@
"""
Verify salt-ssh fails with a retcode > 0 when a highstate verification fails.
``state.show_highstate`` does not validate this.
"""
import pytest
from salt.defaults.exitcodes import EX_AGGREGATE
pytestmark = [
pytest.mark.skip_on_windows(reason="salt-ssh not available on Windows"),
pytest.mark.slow_test,
]
@pytest.fixture(scope="module", autouse=True)
def state_tree_req_fail(base_env_state_tree_root_dir):
top_file = """
base:
'localhost':
- fail_req
'127.0.0.1':
- fail_req
"""
state_file = """
This has an invalid requisite:
test.nop:
- name: foo
- require_in:
- file.managed: invalid_requisite
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
state_tempfile = pytest.helpers.temp_file(
"fail_req.sls", state_file, base_env_state_tree_root_dir
)
with top_tempfile, state_tempfile:
yield
@pytest.mark.parametrize(
"args,retcode",
(
(("state.sls", "fail_req"), EX_AGGREGATE),
(("state.highstate",), EX_AGGREGATE),
(("state.show_sls", "fail_req"), EX_AGGREGATE),
(("state.show_low_sls", "fail_req"), EX_AGGREGATE),
# state.show_lowstate exits with 0 for non-ssh as well
(("state.show_lowstate",), 0),
(("state.top", "top.sls"), EX_AGGREGATE),
),
)
def test_it(salt_ssh_cli, args, retcode):
ret = salt_ssh_cli.run(*args)
assert ret.returncode == retcode
assert isinstance(ret.data, list)
assert ret.data
assert isinstance(ret.data[0], str)
assert ret.data[0].startswith(
"Invalid requisite in require: file.managed for invalid_requisite"
)

View file

@ -0,0 +1,64 @@
"""
Verify salt-ssh fails with a retcode > 0 when a highstate verification fails.
This targets another step of the verification.
``state.sls_id`` does not seem to support extends.
``state.show_highstate`` does not validate this.
"""
import pytest
from salt.defaults.exitcodes import EX_AGGREGATE
pytestmark = [
pytest.mark.skip_on_windows(reason="salt-ssh not available on Windows"),
pytest.mark.slow_test,
]
@pytest.fixture(scope="module", autouse=True)
def state_tree_structure_fail(base_env_state_tree_root_dir):
top_file = """
base:
'localhost':
- fail_structure
'127.0.0.1':
- fail_structure
"""
state_file = """
extend:
Some file state:
file:
- name: /tmp/bar
- contents: bar
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
state_tempfile = pytest.helpers.temp_file(
"fail_structure.sls", state_file, base_env_state_tree_root_dir
)
with top_tempfile, state_tempfile:
yield
@pytest.mark.parametrize(
"args,retcode",
(
(("state.sls", "fail_structure"), EX_AGGREGATE),
(("state.highstate",), EX_AGGREGATE),
(("state.show_sls", "fail_structure"), EX_AGGREGATE),
(("state.show_low_sls", "fail_structure"), EX_AGGREGATE),
# state.show_lowstate exits with 0 for non-ssh as well
(("state.show_lowstate",), 0),
(("state.top", "top.sls"), EX_AGGREGATE),
),
)
def test_it(salt_ssh_cli, args, retcode):
ret = salt_ssh_cli.run(*args)
assert ret.returncode == retcode
assert isinstance(ret.data, list)
assert ret.data
assert isinstance(ret.data[0], str)
assert ret.data[0].startswith(
"Cannot extend ID 'Some file state' in 'base:fail_structure"
)

View file

@ -0,0 +1,57 @@
"""
Verify salt-ssh fails with a retcode > 0 when a pillar rendering fails.
"""
import pytest
from salt.defaults.exitcodes import EX_AGGREGATE
pytestmark = [
pytest.mark.skip_on_windows(reason="salt-ssh not available on Windows"),
pytest.mark.slow_test,
]
@pytest.fixture(scope="module", autouse=True)
def pillar_tree_render_fail(base_env_pillar_tree_root_dir):
top_file = """
base:
'localhost':
- fail_render
'127.0.0.1':
- fail_render
"""
pillar_file = r"""
not_defined: {{ abc }}
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_pillar_tree_root_dir
)
pillar_tempfile = pytest.helpers.temp_file(
"fail_render.sls", pillar_file, base_env_pillar_tree_root_dir
)
with top_tempfile, pillar_tempfile:
yield
@pytest.mark.parametrize(
"args",
(
("state.sls", "basic"),
("state.highstate",),
("state.sls_id", "foo", "basic"),
("state.show_sls", "basic"),
("state.show_low_sls", "basic"),
("state.show_highstate",),
("state.show_lowstate",),
("state.top", "top.sls"),
),
)
def test_it(salt_ssh_cli, args):
ret = salt_ssh_cli.run(*args)
assert ret.returncode == EX_AGGREGATE
assert isinstance(ret.data, list)
assert ret.data
assert isinstance(ret.data[0], str)
assert ret.data[0] == "Pillar failed to render with the following messages:"
assert ret.data[1].startswith("Rendering SLS 'fail_render' failed.")

View file

@ -0,0 +1,67 @@
"""
Verify salt-ssh fails with a retcode > 0 when a state rendering fails.
"""
import pytest
from salt.defaults.exitcodes import EX_AGGREGATE
pytestmark = [
pytest.mark.skip_on_windows(reason="salt-ssh not available on Windows"),
pytest.mark.slow_test,
]
@pytest.fixture(scope="module", autouse=True)
def state_tree_render_fail(base_env_state_tree_root_dir):
top_file = """
base:
'localhost':
- fail_render
'127.0.0.1':
- fail_render
"""
state_file = r"""
abc var is not defined {{ abc }}:
test.nop
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
state_tempfile = pytest.helpers.temp_file(
"fail_render.sls", state_file, base_env_state_tree_root_dir
)
with top_tempfile, state_tempfile:
yield
@pytest.mark.parametrize(
"args,retcode",
(
(("state.sls", "fail_render"), EX_AGGREGATE),
(("state.highstate",), EX_AGGREGATE),
(("state.sls_id", "foo", "fail_render"), EX_AGGREGATE),
(("state.show_sls", "fail_render"), EX_AGGREGATE),
(("state.show_low_sls", "fail_render"), EX_AGGREGATE),
(("state.show_highstate",), EX_AGGREGATE),
# state.show_lowstate exits with 0 for non-ssh as well
(("state.show_lowstate",), 0),
(("state.top", "top.sls"), EX_AGGREGATE),
),
)
def test_it(salt_ssh_cli, args, retcode):
ret = salt_ssh_cli.run(*args)
assert ret.returncode == retcode
assert isinstance(ret.data, list)
assert ret.data
assert isinstance(ret.data[0], str)
assert ret.data[0].startswith(
"Rendering SLS 'base:fail_render' failed: Jinja variable 'abc' is undefined;"
)
def test_state_single(salt_ssh_cli):
ret = salt_ssh_cli.run("state.single", "file")
assert ret.returncode == EX_AGGREGATE
assert isinstance(ret.data, str)
assert "single() missing 1 required positional argument" in ret.data

View file

@ -0,0 +1,52 @@
"""
Verify salt-ssh passes on a failing retcode from state execution.
"""
import pytest
from salt.defaults.exitcodes import EX_AGGREGATE
pytestmark = [
pytest.mark.skip_on_windows(reason="salt-ssh not available on Windows"),
pytest.mark.slow_test,
]
@pytest.fixture(scope="module", autouse=True)
def state_tree_run_fail(base_env_state_tree_root_dir):
top_file = """
base:
'localhost':
- fail_run
'127.0.0.1':
- fail_run
"""
state_file = """
This file state fails:
file.managed:
- name: /tmp/non/ex/is/tent
- makedirs: false
- contents: foo
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
state_tempfile = pytest.helpers.temp_file(
"fail_run.sls", state_file, base_env_state_tree_root_dir
)
with top_tempfile, state_tempfile:
yield
@pytest.mark.parametrize(
"args",
(
("state.sls", "fail_run"),
("state.highstate",),
("state.sls_id", "This file state fails", "fail_run"),
("state.top", "top.sls"),
),
)
def test_it(salt_ssh_cli, args):
ret = salt_ssh_cli.run(*args)
assert ret.returncode == EX_AGGREGATE

View file

@ -0,0 +1,103 @@
import pytest
pytestmark = [
pytest.mark.skip_on_windows(reason="salt-ssh not available on Windows"),
pytest.mark.slow_test,
]
def test_state_with_import(salt_ssh_cli, state_tree):
"""
verify salt-ssh can use imported map files in states
"""
ret = salt_ssh_cli.run("state.sls", "test")
assert ret.returncode == 0
assert ret.data
@pytest.mark.parametrize(
"ssh_cmd",
[
"state.sls",
"state.highstate",
"state.apply",
"state.show_top",
"state.show_highstate",
"state.show_low_sls",
"state.show_lowstate",
"state.sls_id",
"state.show_sls",
"state.top",
],
)
def test_state_with_import_dir(salt_ssh_cli, state_tree_dir, ssh_cmd):
"""
verify salt-ssh can use imported map files in states
when the map files are in another directory outside of
sls files importing them.
"""
if ssh_cmd in ("state.sls", "state.show_low_sls", "state.show_sls"):
ret = salt_ssh_cli.run("-w", "-t", ssh_cmd, "test")
elif ssh_cmd == "state.top":
ret = salt_ssh_cli.run("-w", "-t", ssh_cmd, "top.sls")
elif ssh_cmd == "state.sls_id":
ret = salt_ssh_cli.run("-w", "-t", ssh_cmd, "Ok with def", "test")
else:
ret = salt_ssh_cli.run("-w", "-t", ssh_cmd)
assert ret.returncode == 0
if ssh_cmd == "state.show_top":
assert ret.data == {"base": ["test", "master_tops_test"]} or {"base": ["test"]}
elif ssh_cmd in ("state.show_highstate", "state.show_sls"):
assert ret.data == {
"Ok with def": {
"__sls__": "test",
"__env__": "base",
"test": ["succeed_without_changes", {"order": 10000}],
}
}
elif ssh_cmd in ("state.show_low_sls", "state.show_lowstate", "state.show_sls"):
assert ret.data == [
{
"state": "test",
"name": "Ok with def",
"__sls__": "test",
"__env__": "base",
"__id__": "Ok with def",
"order": 10000,
"fun": "succeed_without_changes",
}
]
else:
assert ret.data["test_|-Ok with def_|-Ok with def_|-succeed_without_changes"][
"result"
]
assert ret.data
def test_state_with_import_from_dir(salt_ssh_cli, nested_state_tree):
"""
verify salt-ssh can use imported map files in states
"""
ret = salt_ssh_cli.run(
"--extra-filerefs=salt://foo/map.jinja", "state.apply", "foo"
)
assert ret.returncode == 0
assert ret.data
def test_state_low(salt_ssh_cli):
"""
test state.low with salt-ssh
"""
ret = salt_ssh_cli.run(
"state.low", '{"state": "cmd", "fun": "run", "name": "echo blah"}'
)
assert ret.data["cmd_|-echo blah_|-echo blah_|-run"]["changes"]["stdout"] == "blah"
def test_state_high(salt_ssh_cli):
"""
test state.high with salt-ssh
"""
ret = salt_ssh_cli.run("state.high", '{"echo blah": {"cmd": ["run"]}}')
assert ret.data["cmd_|-echo blah_|-echo blah_|-run"]["changes"]["stdout"] == "blah"

View file

@ -0,0 +1,241 @@
import logging
import pathlib
import shutil
import subprocess
import textwrap
import pytest
from pytestshellutils.utils.processes import ProcessResult
log = logging.getLogger(__name__)
# The following fixtures are copied from pytests/functional/pillar/test_gpg.py
@pytest.fixture(scope="module")
def test_key():
"""
Private key for setting up GPG pillar environment.
"""
return textwrap.dedent(
"""\
-----BEGIN PGP PRIVATE KEY BLOCK-----
lQOYBFiKrcYBCADAj92+fz20uKxxH0ffMwcryGG9IogkiUi2QrNYilB4hwrY5Qt7
Sbywlk/mSDMcABxMxS0vegqc5pgglvAnsi9w7j//9nfjiirsyiTYOOD1akTFQr7b
qT6zuGFA4oYmYHvfBOena485qvlyitYLKYT9h27TDiiH6Jgt4xSRbjeyhTf3/fKD
JzHA9ii5oeVi1pH/8/4USgXanBdKwO0JKQtci+PF0qe/nkzRswqTIkdgx1oyNUqL
tYJ0XPOy+UyOC4J4QDIt9PQbAmiur8By4g2lLYWlGOCjs7Fcj3n5meWKzf1pmXoY
lAnSab8kUZSSkoWQoTO7RbjFypULKCZui45/ABEBAAEAB/wM1wsAMtfYfx/wgxd1
yJ9HyhrKU80kMotIq/Xth3uKLecJQ2yakfYlCEDXqCTQTymT7OnwaoDeqXmnYqks
3HLRYvGdjb+8ym/GTkxapqBJfQaM6MB1QTnPHhJOE0zCrlhULK2NulxYihAMFTnk
kKYviaJYLG+DcH0FQkkS0XihTKcqnsoJiS6iNd5SME3pa0qijR0D5f78fkvNzzEE
9vgAX1TgQ5PDJGN6nYlW2bWxTcg+FR2cUAQPTiP9wXCH6VyJoQay7KHVr3r/7SsU
89otfcx5HVDYPrez6xnP6wN0P/mKxCDbkERLDjZjWOmNXg2zn+/t3u02e+ybfAIp
kTTxBADY/FmPgLpJ2bpcPH141twpHwhKIbENlTB9745Qknr6aLA0QVCkz49/3joO
Sj+SZ7Jhl6cfbynrfHwX3b1bOFTzBUH2Tsi0HX40PezEFH0apf55FLZuMOBt/lc1
ET6evpIHF0dcM+BvZa7E7MyTyEq8S7Cc9RoJyfeGbS7MG5FfuwQA4y9QOb/OQglq
ZffkVItwY52RKWb/b2WQmt+IcVax/j7DmBva765SIfPDvOCMrYhJBI/uYHQ0Zia7
SnC9+ez55wdYqgHkYojc21CIOnUvsPSj+rOpryoXzmcTuvKeVIyIA0h/mQyWjimR
ENrikC4+O8GBMY6V4uvS4EFhLfHE9g0D/20lNOKkpAKPenr8iAPWcl0/pijJCGxF
agnT7O2GQ9Lr5hSjW86agkevbGktu2ja5t/fHq0wpLQ4DVLMrR0/poaprTr307kW
AlQV3z/C2cMHNysz4ulOgQrudQbhUEz2A8nQxRtIfWunkEugKLr1QiCkE1LJW8Np
ZLxE6Qp0/KzdQva0HVNhbHQgR1BHIDxlcmlrQHNhbHRzdGFjay5jb20+iQFUBBMB
CAA+FiEE+AxQ1ELHGEyFTZPYw5x3k9EbHGsFAliKrcYCGwMFCQPCZwAFCwkIBwIG
FQgJCgsCBBYCAwECHgECF4AACgkQw5x3k9EbHGubUAf+PLdp1oTLVokockZgLyIQ
wxOd3ofNOgNk4QoAkSMNSbtnYoQFKumRw/yGyPSIoHMsOC/ga98r8TAJEKfx3DLA
rsD34oMAaYUT+XUd0KoSmlHqBrtDD1+eBASKYsCosHpCiKuQFfLKSxvpEr2YyL8L
X3Q2TY5zFlGA9Eeq5g+rlb++yRZrruFN28EWtY/pyXFZgIB30ReDwPkM9hrioPZM
0Qf3+dWZSK1rWViclB51oNy4un9stTiFZptAqz4NTNssU5A4AcNQPwBwnKIYoE58
Y/Zyv8HzILGykT+qFebqRlRBI/13eHdzgJOL1iPRfjTk5Cvr+vcyIxAklXOP81ja
B50DmARYiq3GAQgArnzu4SPCCQGNcCNxN4QlMP5TNvRsm5KrPbcO9j8HPfB+DRXs
6B3mnuR6OJg7YuC0C2A/m2dSHJKkF0f2AwFRpxLjJ2iAFbrZAW/N0vZDx8zO+YAU
HyLu0V04wdCE5DTLkgfWNR+0uMa8qZ4Kn56Gv7O+OFE7zgTHeZ7psWlxdafeW7u6
zlC/3DWksNtuNb0vQDNMM4vgXbnORIfXdyh41zvEEnr/rKw8DuJAmo20mcv6Qi51
PqqyM62ddQOEVfiMs9l4vmwZAjGFNFNInyPXnogL6UPCDmizb6hh8aX/MwG/XFIG
KMJWbAVGpyBuqljKIt3qLu/s8ouPqkEN+f+nGwARAQABAAf+NA36d/kieGxZpTQ1
oQHP1Jty+OiXhBwP8SPtF0J7ZxuZh07cs+zDsfBok/y6bsepfuFSaIq84OBQis+B
kajxkp3cXZPb7l+lQLv5k++7Dd7Ien+ewSE7TQN6HLwYATrM5n5nBcc1M5C6lQGc
mr0A5yz42TVG2bHsTpi9kBtsaVRSPUHSh8A8T6eOyCrT+/CAJVEEf7JyNyaqH1dy
LuxI1VF3ySDEtFzuwN8EZQP9Yz/4AVyEQEA7WkNEwSQsBi2bWgWEdG+qjqnL+YKa
vwe7/aJYPeL1zICnP/Osd/UcpDxR78MbozstbRljML0fTLj7UJ+XDazwv+Kl0193
2ZK2QQQAwgXvS19MYNkHO7kbNVLt1VE2ll901iC9GFHBpFUam6gmoHXpCarB+ShH
8x25aoUu4MxHmFxXd+Zq3d6q2yb57doWoPgvqcefpGmigaITnb1jhV2rt65V8deA
SQazZNqBEBbZNIhfn6ObxHXXvaYaqq/UOEQ7uKyR9WMJT/rmqMEEAOY5h1R1t7AB
JZ5VnhyAhdsNWw1gTcXB3o8gKz4vjdnPm0F4aVIPfB3BukETDc3sc2tKmCfUF7I7
oOrh7iRez5F0RIC3KDzXF8qUuWBfPViww45JgftdKsecCIlEEYCoc+3goX0su2bP
V1MDuHijMGTJCBABDgizNb0oynW5xcrbA/0QnKfpTwi7G3oRcJWv2YebVDRcU+SP
dOYhq6SnmWPizEIljRG/X7FHJB+W7tzryO3sCDTAYwxFrfMwvJ2PwnAYI4349zYd
lC28HowUkBYNhwBXc48xCfyhPZtD0aLx/OX1oLZ/vi8gd8TusgGupV/JjkFVO+Nd
+shN/UEAldwqkkY2iQE8BBgBCAAmFiEE+AxQ1ELHGEyFTZPYw5x3k9EbHGsFAliK
rcYCGwwFCQPCZwAACgkQw5x3k9EbHGu4wwf/dRFat91BRX1TJfwJl5otoAXpItYM
6kdWWf1Eb1BicAvXhI078MSH4WXdKkJjJr1fFP8Ynil513H4Mzb0rotMAhb0jLSA
lSRkMbhMvPxoS2kaYzioaBpp8yXpGiNo7dF+PJXSm/Uwp3AkcFjoVbBOqDWGgxMi
DvDAstzLZ9dIcmr+OmcRQykKOKXlhEl3HnR5CyuPrA8hdVup4oeVwdkJhfJFKLLb
3fR26wxJOmIOAt24eAUy721WfQ9txNAmhdy8mY842ODZESw6WatrQjRfuqosDgrk
jc0cCHsEqJNZ2AB+1uEl3tcH0tyAFJa33F0znSonP17SS1Ff9sgHYBVLUg==
=06Tz
-----END PGP PRIVATE KEY BLOCK-----
"""
)
@pytest.fixture(scope="module")
def gpg_pillar_yaml():
"""
Yaml data for testing GPG pillar.
"""
return textwrap.dedent(
"""
#!yaml|gpg
secrets:
foo: |
-----BEGIN PGP MESSAGE-----
hQEMAw2B674HRhwSAQgAhTrN8NizwUv/VunVrqa4/X8t6EUulrnhKcSeb8sZS4th
W1Qz3K2NjL4lkUHCQHKZVx/VoZY7zsddBIFvvoGGfj8+2wjkEDwFmFjGE4DEsS74
ZLRFIFJC1iB/O0AiQ+oU745skQkU6OEKxqavmKMrKo3rvJ8ZCXDC470+i2/Hqrp7
+KWGmaDOO422JaSKRm5D9bQZr9oX7KqnrPG9I1+UbJyQSJdsdtquPWmeIpamEVHb
VMDNQRjSezZ1yKC4kCWm3YQbBF76qTHzG1VlLF5qOzuGI9VkyvlMaLfMibriqY73
zBbPzf6Bkp2+Y9qyzuveYMmwS4sEOuZL/PetqisWe9JGAWD/O+slQ2KRu9hNww06
KMDPJRdyj5bRuBVE4hHkkP23KrYr7SuhW2vpe7O/MvWEJ9uDNegpMLhTWruGngJh
iFndxegN9w==
=bAuo
-----END PGP MESSAGE-----
"""
)
@pytest.fixture(scope="module")
def gpg_homedir(salt_master, test_key):
"""
Setup gpg environment
"""
_gpg_homedir = pathlib.Path(salt_master.config_dir) / "gpgkeys"
_gpg_homedir.mkdir(0o700)
agent_started = False
try:
cmd_prefix = ["gpg", "--homedir", str(_gpg_homedir)]
cmd = cmd_prefix + ["--list-keys"]
proc = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
check=True,
universal_newlines=True,
)
ret = ProcessResult(
returncode=proc.returncode,
stdout=proc.stdout,
stderr=proc.stderr or "",
cmdline=proc.args,
)
log.debug("Instantiating gpg keyring...\n%s", ret)
cmd = cmd_prefix + ["--import", "--allow-secret-key-import"]
proc = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
check=True,
universal_newlines=True,
input=test_key,
)
ret = ProcessResult(
returncode=proc.returncode,
stdout=proc.stdout,
stderr=proc.stderr or "",
cmdline=proc.args,
)
log.debug("Importing keypair...:\n%s", ret)
agent_started = True
yield _gpg_homedir
finally:
if agent_started:
try:
cmd = ["gpg-connect-agent", "--homedir", str(_gpg_homedir)]
proc = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
check=True,
universal_newlines=True,
input="KILLAGENT",
)
ret = ProcessResult(
returncode=proc.returncode,
stdout=proc.stdout,
stderr=proc.stderr or "",
cmdline=proc.args,
)
log.debug("Killed gpg-agent...\n%s", ret)
except (OSError, subprocess.CalledProcessError):
log.debug("No need to kill: old gnupg doesn't start the agent.")
shutil.rmtree(str(_gpg_homedir), ignore_errors=True)
@pytest.fixture(scope="module")
def pillar_setup(base_env_pillar_tree_root_dir, gpg_pillar_yaml, salt_minion):
"""
Setup gpg pillar
"""
saltutil_contents = f"""
saltutil: {{{{ salt["saltutil.runner"]("mine.get", tgt="{salt_minion.id}", fun="test.ping") | json }}}}
"""
top_file_contents = """
base:
'*':
- gpg
- saltutil
"""
with pytest.helpers.temp_file(
"top.sls", top_file_contents, base_env_pillar_tree_root_dir
), pytest.helpers.temp_file(
"gpg.sls", gpg_pillar_yaml, base_env_pillar_tree_root_dir
), pytest.helpers.temp_file(
"saltutil.sls", saltutil_contents, base_env_pillar_tree_root_dir
):
yield
@pytest.mark.skip_if_binaries_missing("gpg")
@pytest.mark.usefixtures("pillar_setup", "gpg_homedir")
def test_gpg_pillar(salt_ssh_cli):
"""
Ensure that GPG-encrypted pillars can be decrypted, i.e. the
gpg_keydir should not be overridden. This is issue #60002,
which has the same cause as the one below.
"""
ret = salt_ssh_cli.run("pillar.items")
assert ret.returncode == 0
assert isinstance(ret.data, dict)
assert ret.data
assert "secrets" in ret.data
assert "foo" in ret.data["secrets"]
assert "BEGIN PGP MESSAGE" not in ret.data["secrets"]["foo"]
assert ret.data["secrets"]["foo"] == "supersecret"
assert "_errors" not in ret.data
@pytest.mark.usefixtures("pillar_setup")
def test_saltutil_runner(salt_ssh_cli, salt_minion, salt_run_cli):
"""
Ensure that during pillar compilation, the cache dir is not
overridden. For a history, see PR #50489 and issue #36796,
notice that the initial description is probably unrelated
to this.
"""
ret = salt_ssh_cli.run("pillar.items")
assert ret.returncode == 0
assert isinstance(ret.data, dict)
assert ret.data
assert "saltutil" in ret.data
assert isinstance(ret.data["saltutil"], dict)
assert ret.data["saltutil"]
assert salt_minion.id in ret.data["saltutil"]
assert ret.data["saltutil"][salt_minion.id] is True
assert "_errors" not in ret.data

View file

@ -1,563 +0,0 @@
import json
import pytest
from salt.defaults.exitcodes import EX_AGGREGATE
pytestmark = [
pytest.mark.skip_on_windows(reason="salt-ssh not available on Windows"),
]
@pytest.fixture(scope="module")
def state_tree(base_env_state_tree_root_dir):
top_file = """
{%- from "map.jinja" import abc with context %}
base:
'localhost':
- basic
'127.0.0.1':
- basic
"""
map_file = """
{%- set abc = "def" %}
"""
state_file = """
{%- from "map.jinja" import abc with context %}
Ok with {{ abc }}:
test.succeed_without_changes
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
map_tempfile = pytest.helpers.temp_file(
"map.jinja", map_file, base_env_state_tree_root_dir
)
state_tempfile = pytest.helpers.temp_file(
"test.sls", state_file, base_env_state_tree_root_dir
)
with top_tempfile, map_tempfile, state_tempfile:
yield
@pytest.fixture(scope="module")
def state_tree_dir(base_env_state_tree_root_dir):
"""
State tree with files to test salt-ssh
when the map.jinja file is in another directory
"""
top_file = """
{%- from "test/map.jinja" import abc with context %}
base:
'localhost':
- test
'127.0.0.1':
- test
"""
map_file = """
{%- set abc = "def" %}
"""
state_file = """
{%- from "test/map.jinja" import abc with context %}
Ok with {{ abc }}:
test.succeed_without_changes
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
map_tempfile = pytest.helpers.temp_file(
"test/map.jinja", map_file, base_env_state_tree_root_dir
)
state_tempfile = pytest.helpers.temp_file(
"test.sls", state_file, base_env_state_tree_root_dir
)
with top_tempfile, map_tempfile, state_tempfile:
yield
@pytest.fixture(scope="class")
def state_tree_render_fail(base_env_state_tree_root_dir):
top_file = """
base:
'localhost':
- fail_render
'127.0.0.1':
- fail_render
"""
state_file = r"""
abc var is not defined {{ abc }}:
test.nop
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
state_tempfile = pytest.helpers.temp_file(
"fail_render.sls", state_file, base_env_state_tree_root_dir
)
with top_tempfile, state_tempfile:
yield
@pytest.fixture(scope="class")
def state_tree_req_fail(base_env_state_tree_root_dir):
top_file = """
base:
'localhost':
- fail_req
'127.0.0.1':
- fail_req
"""
state_file = """
This has an invalid requisite:
test.nop:
- name: foo
- require_in:
- file.managed: invalid_requisite
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
state_tempfile = pytest.helpers.temp_file(
"fail_req.sls", state_file, base_env_state_tree_root_dir
)
with top_tempfile, state_tempfile:
yield
@pytest.fixture(scope="class")
def state_tree_structure_fail(base_env_state_tree_root_dir):
top_file = """
base:
'localhost':
- fail_structure
'127.0.0.1':
- fail_structure
"""
state_file = """
extend:
Some file state:
file:
- name: /tmp/bar
- contents: bar
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
state_tempfile = pytest.helpers.temp_file(
"fail_structure.sls", state_file, base_env_state_tree_root_dir
)
with top_tempfile, state_tempfile:
yield
@pytest.fixture(scope="class")
def state_tree_run_fail(base_env_state_tree_root_dir):
top_file = """
base:
'localhost':
- fail_run
'127.0.0.1':
- fail_run
"""
state_file = """
This file state fails:
file.managed:
- name: /tmp/non/ex/is/tent
- makedirs: false
- contents: foo
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
state_tempfile = pytest.helpers.temp_file(
"fail_run.sls", state_file, base_env_state_tree_root_dir
)
with top_tempfile, state_tempfile:
yield
@pytest.fixture(scope="class")
def pillar_tree_render_fail(base_env_pillar_tree_root_dir):
top_file = """
base:
'localhost':
- fail_render
'127.0.0.1':
- fail_render
"""
pillar_file = r"""
not_defined: {{ abc }}
"""
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_pillar_tree_root_dir
)
pillar_tempfile = pytest.helpers.temp_file(
"fail_render.sls", pillar_file, base_env_pillar_tree_root_dir
)
with top_tempfile, pillar_tempfile:
yield
@pytest.mark.slow_test
def test_state_with_import(salt_ssh_cli, state_tree):
"""
verify salt-ssh can use imported map files in states
"""
ret = salt_ssh_cli.run("state.sls", "test")
assert ret.returncode == 0
assert ret.data
@pytest.mark.parametrize(
"ssh_cmd",
[
"state.sls",
"state.highstate",
"state.apply",
"state.show_top",
"state.show_highstate",
"state.show_low_sls",
"state.show_lowstate",
"state.sls_id",
"state.show_sls",
"state.top",
],
)
@pytest.mark.slow_test
def test_state_with_import_dir(salt_ssh_cli, state_tree_dir, ssh_cmd):
"""
verify salt-ssh can use imported map files in states
when the map files are in another directory outside of
sls files importing them.
"""
if ssh_cmd in ("state.sls", "state.show_low_sls", "state.show_sls"):
ret = salt_ssh_cli.run("-w", "-t", ssh_cmd, "test")
elif ssh_cmd == "state.top":
ret = salt_ssh_cli.run("-w", "-t", ssh_cmd, "top.sls")
elif ssh_cmd == "state.sls_id":
ret = salt_ssh_cli.run("-w", "-t", ssh_cmd, "Ok with def", "test")
else:
ret = salt_ssh_cli.run("-w", "-t", ssh_cmd)
assert ret.returncode == 0
if ssh_cmd == "state.show_top":
assert ret.data == {"base": ["test", "master_tops_test"]} or {"base": ["test"]}
elif ssh_cmd in ("state.show_highstate", "state.show_sls"):
assert ret.data == {
"Ok with def": {
"__sls__": "test",
"__env__": "base",
"test": ["succeed_without_changes", {"order": 10000}],
}
}
elif ssh_cmd in ("state.show_low_sls", "state.show_lowstate", "state.show_sls"):
assert ret.data == [
{
"state": "test",
"name": "Ok with def",
"__sls__": "test",
"__env__": "base",
"__id__": "Ok with def",
"order": 10000,
"fun": "succeed_without_changes",
}
]
else:
assert ret.data["test_|-Ok with def_|-Ok with def_|-succeed_without_changes"][
"result"
]
assert ret.data
@pytest.fixture
def nested_state_tree(base_env_state_tree_root_dir, tmp_path):
top_file = """
base:
'localhost':
- basic
'127.0.0.1':
- basic
"""
state_file = """
/{}/file.txt:
file.managed:
- source: salt://foo/file.jinja
- template: jinja
""".format(
tmp_path
)
file_jinja = """
{% from 'foo/map.jinja' import comment %}{{ comment }}
"""
map_file = """
{% set comment = "blah blah" %}
"""
statedir = base_env_state_tree_root_dir / "foo"
top_tempfile = pytest.helpers.temp_file(
"top.sls", top_file, base_env_state_tree_root_dir
)
map_tempfile = pytest.helpers.temp_file("map.jinja", map_file, statedir)
file_tempfile = pytest.helpers.temp_file("file.jinja", file_jinja, statedir)
state_tempfile = pytest.helpers.temp_file("init.sls", state_file, statedir)
with top_tempfile, map_tempfile, state_tempfile, file_tempfile:
yield
@pytest.mark.slow_test
def test_state_with_import_from_dir(salt_ssh_cli, nested_state_tree):
"""
verify salt-ssh can use imported map files in states
"""
ret = salt_ssh_cli.run(
"--extra-filerefs=salt://foo/map.jinja", "state.apply", "foo"
)
assert ret.returncode == 0
assert ret.data
@pytest.mark.slow_test
def test_state_low(salt_ssh_cli):
"""
test state.low with salt-ssh
"""
ret = salt_ssh_cli.run(
"state.low", '{"state": "cmd", "fun": "run", "name": "echo blah"}'
)
assert (
json.loads(ret.stdout)["localhost"]["cmd_|-echo blah_|-echo blah_|-run"][
"changes"
]["stdout"]
== "blah"
)
@pytest.mark.slow_test
def test_state_high(salt_ssh_cli):
"""
test state.high with salt-ssh
"""
ret = salt_ssh_cli.run("state.high", '{"echo blah": {"cmd": ["run"]}}')
assert (
json.loads(ret.stdout)["localhost"]["cmd_|-echo blah_|-echo blah_|-run"][
"changes"
]["stdout"]
== "blah"
)
@pytest.mark.slow_test
@pytest.mark.usefixtures("state_tree_render_fail")
class TestRenderExceptionRetcode:
"""
Verify salt-ssh fails with a retcode > 0 when a state rendering fails.
"""
def test_retcode_state_sls_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.sls", "fail_render")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_highstate_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.highstate")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_sls_id_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.sls_id", "foo", "fail_render")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_show_sls_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_sls", "fail_render")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_show_low_sls_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_low_sls", "fail_render")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_show_highstate_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_highstate")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_show_lowstate_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_lowstate")
# state.show_lowstate exits with 0 for non-ssh as well
self._assert_ret(ret, 0)
def test_retcode_state_top_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.top", "top.sls")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_single_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.single", "file")
assert ret.returncode == EX_AGGREGATE
assert isinstance(ret.data, str)
assert "single() missing 1 required positional argument" in ret.data
def _assert_ret(self, ret, retcode):
assert ret.returncode == retcode
assert isinstance(ret.data, list)
assert ret.data
assert isinstance(ret.data[0], str)
assert ret.data[0].startswith(
"Rendering SLS 'base:fail_render' failed: Jinja variable 'abc' is undefined;"
)
@pytest.mark.slow_test
@pytest.mark.usefixtures("pillar_tree_render_fail")
class TestPillarRenderExceptionRetcode:
"""
Verify salt-ssh fails with a retcode > 0 when a pillar rendering fails.
"""
def test_retcode_state_sls_pillar_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.sls", "basic")
self._assert_ret(ret)
def test_retcode_state_highstate_pillar_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.highstate")
self._assert_ret(ret)
def test_retcode_state_sls_id_pillar_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.sls_id", "foo", "basic")
self._assert_ret(ret)
def test_retcode_state_show_sls_pillar_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_sls", "basic")
self._assert_ret(ret)
def test_retcode_state_show_low_sls_pillar_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_low_sls", "basic")
self._assert_ret(ret)
def test_retcode_state_show_highstate_pillar_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_highstate")
self._assert_ret(ret)
def test_retcode_state_show_lowstate_pillar_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_lowstate")
self._assert_ret(ret)
def test_retcode_state_top_pillar_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.top", "top.sls")
self._assert_ret(ret)
def _assert_ret(self, ret):
assert ret.returncode == EX_AGGREGATE
assert isinstance(ret.data, list)
assert ret.data
assert isinstance(ret.data[0], str)
assert ret.data[0] == "Pillar failed to render with the following messages:"
assert ret.data[1].startswith("Rendering SLS 'fail_render' failed.")
@pytest.mark.slow_test
@pytest.mark.usefixtures("state_tree_req_fail")
class TestStateReqFailRetcode:
"""
Verify salt-ssh fails with a retcode > 0 when a highstate verification fails.
``state.show_highstate`` does not validate this.
"""
def test_retcode_state_sls_invalid_requisite(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.sls", "fail_req")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_highstate_invalid_requisite(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.highstate")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_show_sls_invalid_requisite(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_sls", "fail_req")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_show_low_sls_invalid_requisite(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_low_sls", "fail_req")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_show_lowstate_invalid_requisite(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_lowstate")
# state.show_lowstate exits with 0 for non-ssh as well
self._assert_ret(ret, 0)
def test_retcode_state_top_invalid_requisite(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.top", "top.sls")
self._assert_ret(ret, EX_AGGREGATE)
def _assert_ret(self, ret, retcode):
assert ret.returncode == retcode
assert isinstance(ret.data, list)
assert ret.data
assert isinstance(ret.data[0], str)
assert ret.data[0].startswith(
"Invalid requisite in require: file.managed for invalid_requisite"
)
@pytest.mark.slow_test
@pytest.mark.usefixtures("state_tree_structure_fail")
class TestStateStructureFailRetcode:
"""
Verify salt-ssh fails with a retcode > 0 when a highstate verification fails.
This targets another step of the verification.
``state.sls_id`` does not seem to support extends.
``state.show_highstate`` does not validate this.
"""
def test_retcode_state_sls_invalid_structure(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.sls", "fail_structure")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_highstate_invalid_structure(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.highstate")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_show_sls_invalid_structure(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_sls", "fail_structure")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_show_low_sls_invalid_structure(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_low_sls", "fail_structure")
self._assert_ret(ret, EX_AGGREGATE)
def test_retcode_state_show_lowstate_invalid_structure(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.show_lowstate")
# state.show_lowstate exits with 0 for non-ssh as well
self._assert_ret(ret, 0)
def test_retcode_state_top_invalid_structure(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.top", "top.sls")
self._assert_ret(ret, EX_AGGREGATE)
def _assert_ret(self, ret, retcode):
assert ret.returncode == retcode
assert isinstance(ret.data, list)
assert ret.data
assert isinstance(ret.data[0], str)
assert ret.data[0].startswith(
"Cannot extend ID 'Some file state' in 'base:fail_structure"
)
@pytest.mark.slow_test
@pytest.mark.usefixtures("state_tree_run_fail")
class TestStateRunFailRetcode:
"""
Verify salt-ssh passes on a failing retcode from state execution.
"""
def test_retcode_state_sls_run_fail(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.sls", "fail_run")
assert ret.returncode == EX_AGGREGATE
def test_retcode_state_highstate_run_fail(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.highstate")
assert ret.returncode == EX_AGGREGATE
def test_retcode_state_sls_id_render_exception(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.sls_id", "This file state fails", "fail_run")
assert ret.returncode == EX_AGGREGATE
def test_retcode_state_top_run_fail(self, salt_ssh_cli):
ret = salt_ssh_cli.run("state.top", "top.sls")
assert ret.returncode == EX_AGGREGATE

View file

@ -1,11 +1,3 @@
"""
:synopsis: Unit Tests for Advanced Packaging Tool module 'module.aptpkg'
:platform: Linux
:maturity: develop
versionadded:: 2017.7.0
"""
import copy
import importlib
import logging
@ -24,7 +16,7 @@ from salt.exceptions import (
SaltInvocationError,
)
from salt.utils.odict import OrderedDict
from tests.support.mock import MagicMock, Mock, call, mock_open, patch
from tests.support.mock import MagicMock, Mock, call, patch
try:
from aptsources.sourceslist import ( # pylint: disable=unused-import
@ -1556,31 +1548,35 @@ SERVICE:cups-daemon,390,/usr/sbin/cupsd
]
@pytest.fixture
def _test_sourceslist_multiple_comps_fs(fs):
fs.create_dir("/etc/apt/sources.list.d")
fs.create_file(
"/etc/apt/sources.list",
contents="deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted",
)
yield
@pytest.mark.skipif(
HAS_APTSOURCES is True, reason="Only run test with python3-apt library is missing."
)
@pytest.mark.usefixtures("_test_sourceslist_multiple_comps_fs")
def test_sourceslist_multiple_comps():
"""
Test SourcesList when repo has multiple comps
"""
repo_line = "deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted"
with patch.object(aptpkg, "HAS_APT", return_value=True):
with patch("salt.utils.files.fopen", mock_open(read_data=repo_line)):
with patch("pathlib.Path.is_file", side_effect=[True, False]):
sources = aptpkg.SourcesList()
for source in sources:
assert source.type == "deb"
assert source.uri == "http://archive.ubuntu.com/ubuntu/"
assert source.comps == ["main", "restricted"]
assert source.dist == "focal-updates"
sources = aptpkg.SourcesList()
for source in sources:
assert source.type == "deb"
assert source.uri == "http://archive.ubuntu.com/ubuntu/"
assert source.comps == ["main", "restricted"]
assert source.dist == "focal-updates"
@pytest.mark.skipif(
HAS_APTSOURCES is True, reason="Only run test with python3-apt library is missing."
)
@pytest.mark.parametrize(
"repo_line",
[
@pytest.fixture(
params=(
"deb [ arch=amd64 ] http://archive.ubuntu.com/ubuntu/ focal-updates main restricted",
"deb [arch=amd64 ] http://archive.ubuntu.com/ubuntu/ focal-updates main restricted",
"deb [arch=amd64 test=one ] http://archive.ubuntu.com/ubuntu/ focal-updates main restricted",
@ -1588,24 +1584,31 @@ def test_sourceslist_multiple_comps():
"deb [ arch=amd64,armel test=one ] http://archive.ubuntu.com/ubuntu/ focal-updates main restricted",
"deb [ arch=amd64,armel test=one] http://archive.ubuntu.com/ubuntu/ focal-updates main restricted",
"deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ focal-updates main restricted",
],
)
)
def repo_line(request, fs):
fs.create_dir("/etc/apt/sources.list.d")
fs.create_file("/etc/apt/sources.list", contents=request.param)
yield request.param
@pytest.mark.skipif(
HAS_APTSOURCES is True, reason="Only run test with python3-apt library is missing."
)
def test_sourceslist_architectures(repo_line):
"""
Test SourcesList when architectures is in repo
"""
with patch("salt.utils.files.fopen", mock_open(read_data=repo_line)):
with patch("pathlib.Path.is_file", side_effect=[True, False]):
sources = aptpkg.SourcesList()
for source in sources:
assert source.type == "deb"
assert source.uri == "http://archive.ubuntu.com/ubuntu/"
assert source.comps == ["main", "restricted"]
assert source.dist == "focal-updates"
if "," in repo_line:
assert source.architectures == ["amd64", "armel"]
else:
assert source.architectures == ["amd64"]
sources = aptpkg.SourcesList()
for source in sources:
assert source.type == "deb"
assert source.uri == "http://archive.ubuntu.com/ubuntu/"
assert source.comps == ["main", "restricted"]
assert source.dist == "focal-updates"
if "," in repo_line:
assert source.architectures == ["amd64", "armel"]
else:
assert source.architectures == ["amd64"]
@pytest.mark.parametrize(

View file

@ -1,9 +1,19 @@
import logging
import msgpack
import pytest
import salt.config
import salt.transport.base
import salt.transport.zeromq
log = logging.getLogger(__name__)
pytestmark = [
pytest.mark.core_test,
]
async def test_req_server_garbage_request(io_loop):
"""