Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package salt for openSUSE:Factory checked in at 2021-09-20 23:32:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/salt (Old) and /work/SRC/openSUSE:Factory/.salt.new.1899 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "salt" Mon Sep 20 23:32:14 2021 rev:118 rq:919452 version:3002.2 Changes: -------- --- /work/SRC/openSUSE:Factory/salt/salt.changes 2021-04-22 18:03:37.474475050 +0200 +++ /work/SRC/openSUSE:Factory/.salt.new.1899/salt.changes 2021-09-20 23:33:00.271162557 +0200 @@ -1,0 +2,184 @@ +Wed Sep 15 11:18:58 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- Exclude the full path of a download URL to prevent injection of + malicious code (bsc#1190265) (CVE-2021-21996) + +- Added: + * exclude-the-full-path-of-a-download-url-to-prevent-i.patch + +------------------------------------------------------------------- +Tue Aug 31 11:28:13 UTC 2021 - Victor Zhestkov <victor.zhest...@suse.com> + +- Fix wrong relative paths resolution with Jinja renderer when importing subdirectories + +- Added: + * templates-move-the-globals-up-to-the-environment-jin.patch + +------------------------------------------------------------------- +Thu Aug 19 14:41:12 UTC 2021 - Victor Zhestkov <victor.zhest...@suse.com> + +- Don't pass shell="/sbin/nologin" to onlyif/unless checks (bsc#1188259) +- Add missing aarch64 to rpm package architectures +- Backport of upstream PR#59492 + +- Added: + * backport-of-upstream-pr59492-to-3002.2-404.patch + * don-t-use-shell-sbin-nologin-in-requisites.patch + * add-missing-aarch64-to-rpm-package-architectures-405.patch + +------------------------------------------------------------------- +Wed Aug 11 12:22:24 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- Fix failing unit test for systemd +- Fix error handling in openscap module (bsc#1188647) +- Better handling of bad public keys from minions (bsc#1189040) + +- Added: + * better-handling-of-bad-public-keys-from-minions-bsc-.patch + * fix-error-handling-in-openscap-module-bsc-1188647-40.patch + * fix-failing-unit-tests-for-systemd.patch + +------------------------------------------------------------------- +Tue Aug 10 12:59:25 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- Define license macro as doc in spec file if not existing +- Add standalone formulas configuration for salt minion and remove salt-master requirement (bsc#1168327) + +------------------------------------------------------------------- +Fri Jul 16 15:35:10 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- Do noop for services states when running systemd in offline mode (bsc#1187787) +- transactional_updates: do not execute states in parallel but use a queue (bsc#1188170) + +- Added: + * do-noop-for-services-states-when-running-systemd-in-.patch + +------------------------------------------------------------------- +Thu Jul 8 08:06:40 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- Handle "master tops" data when states are applied by "transactional_update" (bsc#1187787) +- Enhance openscap module: add "xccdf_eval" call + +- Added: + * enhance-openscap-module-add-xccdf_eval-call-386.patch + * handle-master-tops-data-when-states-are-applied-by-t.patch + +------------------------------------------------------------------- +Tue Jul 6 08:00:23 UTC 2021 - Victor Zhestkov <victor.zhest...@suse.com> + +- virt: pass emulator when getting domain capabilities from libvirt +- Adding preliminary support for Rocky Linux +- Implementation of held/unheld functions for state pkg (bsc#1187813) + +- Added: + * implementation-of-held-unheld-functions-for-state-pk.patch + * adding-preliminary-support-for-rocky.-59682-391.patch + * virt-pass-emulator-when-getting-domain-capabilities-.patch + +------------------------------------------------------------------- +Fri Jun 25 11:54:13 UTC 2021 - Alexander Graul <alexander.gr...@suse.com> + +- Replace deprecated Thread.isAlive() with Thread.is_alive() + +- Added: + * backport-thread.is_alive-fix-390.patch + +------------------------------------------------------------------- +Thu Jun 24 12:41:03 UTC 2021 - Victor Zhestkov <victor.zhest...@suse.com> + +- Fix exception in yumpkg.remove for not installed package +- Fix save for iptables state module (bsc#1185131) + +- Added: + * fix-exception-in-yumpkg.remove-for-not-installed-pac.patch + * fix-save-for-iptables-state-module-bsc-1185131-372.patch + +------------------------------------------------------------------- +Thu Jun 24 09:44:36 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- virt: use /dev/kvm to detect KVM + +- Added: + * virt-use-dev-kvm-to-detect-kvm-383.patch + +------------------------------------------------------------------- +Thu Jun 24 08:41:31 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- zypperpkg: improve logic for handling vendorchange flags + +- Added: + * move-vendor-change-logic-to-zypper-class-355.patch + +------------------------------------------------------------------- +Mon Jun 21 14:57:02 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- Add bundled provides for tornado to the spec file +- Enhance logging when inotify beacon is missing pyinotify (bsc#1186310) +- Add "python3-pyinotify" as a recommended package for Salt in SUSE/OpenSUSE distros + +- Added: + * enhance-logging-when-inotify-beacon-is-missing-pyino.patch + +------------------------------------------------------------------- +Fri Jun 4 09:00:07 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- Fix tmpfiles.d configuration for salt to not use legacy paths (bsc#1173103) + +------------------------------------------------------------------- +Tue Jun 1 12:05:20 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- Check if dpkgnotify is executable (bsc#1186674) + +- Added: + * check-if-dpkgnotify-is-executable-bsc-1186674-376.patch + +------------------------------------------------------------------- +Fri May 21 15:01:10 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- Detect Python version to use inside container (bsc#1167586) (bsc#1164192) +- Handle volumes on stopped pools in virt.vm_info (bsc#1186287) +- Drop support for Python2. Obsoletes "python2-salt" package + +- Added: + * handle-volumes-on-stopped-pools-in-virt.vm_info-373.patch + * figure-out-python-interpreter-to-use-inside-containe.patch + +------------------------------------------------------------------- +Mon May 10 14:45:26 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- grains.extra: support old non-intel kernels (bsc#1180650) +- Fix missing minion returns in batch mode (bsc#1184659) + +- Added: + * fix-missing-minion-returns-in-batch-mode-360.patch + * grains.extra-support-old-non-intel-kernels-bsc-11806.patch + +------------------------------------------------------------------- +Tue May 4 13:44:13 UTC 2021 - Jochen Breuer <jbre...@suse.de> + +- Parsing Epoch out of version provided during pkg remove (bsc#1173692) + +- Added: + * parsing-epoch-out-of-version-provided-during-pkg-rem.patch + +------------------------------------------------------------------- +Tue Apr 27 15:02:30 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- Fix issue parsing errors in ansiblegate state module + +- Added: + * fix-issue-parsing-errors-in-ansiblegate-state-module.patch + +------------------------------------------------------------------- +Tue Apr 27 12:27:17 UTC 2021 - Pablo Su??rez Hern??ndez <pablo.suarezhernan...@suse.com> + +- Prevent command injection in the snapper module (bsc#1185281) (CVE-2021-31607) +- transactional_update: detect recursion in the executor +- Add subpackage salt-transactional-update (jsc#SLE-18028) +- Remove duplicate directories from specfile + +- Added: + * transactional_update-detect-recursion-in-the-executo.patch + * prevent-command-injection-in-the-snapper-module-bsc-.patch + +------------------------------------------------------------------- @@ -4 +188 @@ -- Improvements on "ansiblegate" module: +- Improvements on "ansiblegate" module (bsc#1185092): @@ -223 +407 @@ -- Update to Salt release version 3002.2 +- Update to Salt release version 3002.2 (jsc#ECO-3212) (jsc#SLE-18033) New: ---- add-missing-aarch64-to-rpm-package-architectures-405.patch adding-preliminary-support-for-rocky.-59682-391.patch backport-of-upstream-pr59492-to-3002.2-404.patch backport-thread.is_alive-fix-390.patch better-handling-of-bad-public-keys-from-minions-bsc-.patch check-if-dpkgnotify-is-executable-bsc-1186674-376.patch do-noop-for-services-states-when-running-systemd-in-.patch don-t-use-shell-sbin-nologin-in-requisites.patch enhance-logging-when-inotify-beacon-is-missing-pyino.patch enhance-openscap-module-add-xccdf_eval-call-386.patch exclude-the-full-path-of-a-download-url-to-prevent-i.patch figure-out-python-interpreter-to-use-inside-containe.patch fix-error-handling-in-openscap-module-bsc-1188647-40.patch fix-exception-in-yumpkg.remove-for-not-installed-pac.patch fix-failing-unit-tests-for-systemd.patch fix-issue-parsing-errors-in-ansiblegate-state-module.patch fix-missing-minion-returns-in-batch-mode-360.patch fix-save-for-iptables-state-module-bsc-1185131-372.patch grains.extra-support-old-non-intel-kernels-bsc-11806.patch handle-master-tops-data-when-states-are-applied-by-t.patch handle-volumes-on-stopped-pools-in-virt.vm_info-373.patch implementation-of-held-unheld-functions-for-state-pk.patch move-vendor-change-logic-to-zypper-class-355.patch parsing-epoch-out-of-version-provided-during-pkg-rem.patch prevent-command-injection-in-the-snapper-module-bsc-.patch templates-move-the-globals-up-to-the-environment-jin.patch transactional_update-detect-recursion-in-the-executo.patch transactional_update.conf virt-pass-emulator-when-getting-domain-capabilities-.patch virt-use-dev-kvm-to-detect-kvm-383.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ salt.spec ++++++ --- /var/tmp/diff_new_pack.WWBdJC/_old 2021-09-20 23:33:02.823165710 +0200 +++ /var/tmp/diff_new_pack.WWBdJC/_new 2021-09-20 23:33:02.827165715 +0200 @@ -1,7 +1,7 @@ # # spec file for package salt # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -48,6 +48,7 @@ Source3: html.tar.bz2 Source4: update-documentation.sh Source5: travis.yml +Source6: transactional_update.conf Patch1: run-salt-master-as-dedicated-salt-user.patch Patch2: run-salt-api-as-user-salt-bsc-1064520.patch @@ -396,6 +397,66 @@ Patch167: regression-fix-of-salt-ssh-on-processing-targets-353.patch # PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60056 Patch168: improvements-on-ansiblegate-module-354.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58520 +Patch169: transactional_update-detect-recursion-in-the-executo.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59648 +Patch170: prevent-command-injection-in-the-snapper-module-bsc-.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60056 +Patch171: fix-issue-parsing-errors-in-ansiblegate-state-module.patch +# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/376 +Patch172: check-if-dpkgnotify-is-executable-bsc-1186674-376.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/57881 +Patch173: parsing-epoch-out-of-version-provided-during-pkg-rem.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58503 +Patch174: fix-missing-minion-returns-in-batch-mode-360.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58520 +Patch175: grains.extra-support-old-non-intel-kernels-bsc-11806.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60133 +Patch176: handle-volumes-on-stopped-pools-in-virt.vm_info-373.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60229 +Patch177: figure-out-python-interpreter-to-use-inside-containe.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60402 +Patch178: enhance-logging-when-inotify-beacon-is-missing-pyino.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60421 +Patch179: move-vendor-change-logic-to-zypper-class-355.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60420 +Patch180: virt-use-dev-kvm-to-detect-kvm-383.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60358 +Patch181: fix-save-for-iptables-state-module-bsc-1185131-372.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60356 +Patch182: fix-exception-in-yumpkg.remove-for-not-installed-pac.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59535 +Patch183: backport-thread.is_alive-fix-390.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/56935 +# https://github.com/saltstack/salt/pull/60432 +Patch184: implementation-of-held-unheld-functions-for-state-pk.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59682 +Patch185: adding-preliminary-support-for-rocky.-59682-391.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60492 +Patch186: virt-pass-emulator-when-getting-domain-capabilities-.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59756 +Patch187: enhance-openscap-module-add-xccdf_eval-call-386.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58520 +Patch188: handle-master-tops-data-when-states-are-applied-by-t.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58520 +Patch189: do-noop-for-services-states-when-running-systemd-in-.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60662 +# https://github.com/saltstack/salt/pull/60688 +Patch190: better-handling-of-bad-public-keys-from-minions-bsc-.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59756 +Patch191: fix-error-handling-in-openscap-module-bsc-1188647-40.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/58520 +Patch192: fix-failing-unit-tests-for-systemd.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/59492 +Patch193: backport-of-upstream-pr59492-to-3002.2-404.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60650 +Patch194: add-missing-aarch64-to-rpm-package-architectures-405.patch +# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/413 +Patch195: don-t-use-shell-sbin-nologin-in-requisites.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/60811 +Patch196: templates-move-the-globals-up-to-the-environment-jin.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/commit/0b75ba190fda9c04cc026ad1aa4a6d572f40349b +Patch197: exclude-the-full-path-of-a-download-url-to-prevent-i.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRequires: logrotate @@ -404,6 +465,7 @@ %endif Requires: python3-%{name} = %{version}-%{release} +Obsoletes: python2-%{name} Requires(pre): %{_sbindir}/groupadd Requires(pre): %{_sbindir}/useradd @@ -570,8 +632,11 @@ Requires: python3-xml Suggests: python3-Mako Recommends: python3-netaddr +Recommends: python3-pyinotify %endif +Provides: bundled(python3-tornado) = 4.5.3 + %description -n python3-salt Python3 specific files for salt @@ -648,6 +713,9 @@ Summary: The client component for Saltstack Group: System/Management Requires: %{name} = %{version}-%{release} +%if 0%{?suse_version} > 1500 || 0%{?sle_version} > 150000 +Requires: (%{name}-transactional-update = %{version}-%{release} if read-only-root-fs) +%endif %if %{with systemd} %{?systemd_requires} @@ -778,19 +846,30 @@ %package standalone-formulas-configuration Summary: Standalone Salt configuration to make the packaged formulas available for the Salt master Group: System/Management -Requires: %{name} = %{version}-%{release} -Requires: %{name}-master = %{version}-%{release} +Requires: %{name} Provides: salt-formulas-configuration Conflicts: otherproviders(salt-formulas-configuration) %description standalone-formulas-configuration This package adds the standalone configuration for the Salt master in order to make the packaged Salt formulas available on the Salt master +%package transactional-update +Summary: Transactional update executor configuration +Group: System/Management +Requires: %{name} = %{version}-%{release} +Requires: %{name}-minion = %{version}-%{release} + +%description transactional-update +For transactional systems, like MicroOS, Salt can operate +transparently if the executor "transactional-update" is registered in +list of active executors. This package add the configuration file. + %prep %setup -q -n salt-%{version}-suse cp %{S:1} . cp %{S:5} ./.travis.yml +cp %{S:6} . %patch1 -p1 %patch2 -p1 %patch3 -p1 @@ -959,6 +1038,35 @@ %patch166 -p1 %patch167 -p1 %patch168 -p1 +%patch169 -p1 +%patch170 -p1 +%patch171 -p1 +%patch172 -p1 +%patch173 -p1 +%patch174 -p1 +%patch175 -p1 +%patch176 -p1 +%patch177 -p1 +%patch178 -p1 +%patch179 -p1 +%patch180 -p1 +%patch181 -p1 +%patch182 -p1 +%patch183 -p1 +%patch184 -p1 +%patch185 -p1 +%patch186 -p1 +%patch187 -p1 +%patch188 -p1 +%patch189 -p1 +%patch190 -p1 +%patch191 -p1 +%patch192 -p1 +%patch193 -p1 +%patch194 -p1 +%patch195 -p1 +%patch196 -p1 +%patch197 -p1 %build # Putting /usr/bin at the front of $PATH is needed for RHEL/RES 7. Without this @@ -997,16 +1105,7 @@ done ## create missing directories -install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/master.d -install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/minion.d -install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/cloud.maps.d -install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/cloud.profiles.d -install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/cloud.providers.d -install -Dd -m 0750 %{buildroot}%{_localstatedir}/log/salt -install -Dd -m 0755 %{buildroot}%{_sysconfdir}/logrotate.d/ -install -Dd -m 0755 %{buildroot}%{_sbindir} -install -Dd -m 0750 %{buildroot}%{_localstatedir}/log/salt -install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/minion/extmod +install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/cloud install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/jobs install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/proc @@ -1014,12 +1113,8 @@ install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/roots install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/syndics install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/master/tokens -install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/cloud -install -Dd -m 0750 %{buildroot}/var/lib/salt -install -Dd -m 0750 %{buildroot}/srv/salt -install -Dd -m 0750 %{buildroot}/srv/pillar -install -Dd -m 0750 %{buildroot}/srv/spm -install -Dd -m 0755 %{buildroot}%{_docdir}/salt +install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/minion/extmod +install -Dd -m 0750 %{buildroot}%{_localstatedir}/log/salt install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/ install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/cloud.maps.d install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/cloud.profiles.d @@ -1034,6 +1129,13 @@ install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/pki/master/minions_pre install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/pki/master/minions_rejected install -Dd -m 0750 %{buildroot}%{_sysconfdir}/salt/pki/minion +install -Dd -m 0750 %{buildroot}/srv/pillar +install -Dd -m 0750 %{buildroot}/srv/salt +install -Dd -m 0750 %{buildroot}/srv/spm +install -Dd -m 0750 %{buildroot}/var/lib/salt +install -Dd -m 0755 %{buildroot}%{_docdir}/salt +install -Dd -m 0755 %{buildroot}%{_sbindir} +install -Dd -m 0755 %{buildroot}%{_sysconfdir}/logrotate.d/ # Install salt-support profiles install -Dpm 0644 salt/cli/support/profiles/* %{buildroot}%{python3_sitelib}/salt/cli/support/profiles @@ -1097,6 +1199,7 @@ install -Dpm 0640 conf/cloud %{buildroot}%{_sysconfdir}/salt/cloud install -Dpm 0640 conf/cloud.profiles %{buildroot}%{_sysconfdir}/salt/cloud.profiles install -Dpm 0640 conf/cloud.providers %{buildroot}%{_sysconfdir}/salt/cloud.providers +install -Dpm 0640 transactional_update.conf %{buildroot}%{_sysconfdir}/salt/minion.d/transactional_update.conf # ## install logrotate file (for RHEL6 we use without sudo) %if 0%{?rhel} > 6 || 0%{?suse_version} @@ -1128,6 +1231,7 @@ install -Dd -m 0750 %{buildroot}%{_prefix}/share/salt-formulas/states install -Dd -m 0750 %{buildroot}%{_prefix}/share/salt-formulas/metadata install -Dpm 0640 conf/suse/standalone-formulas-configuration.conf %{buildroot}%{_sysconfdir}/salt/master.d +install -Dpm 0640 conf/suse/standalone-formulas-configuration.conf %{buildroot}%{_sysconfdir}/salt/minion.d %if 0%{?suse_version} > 1020 %fdupes %{buildroot}%{_docdir} @@ -1580,12 +1684,9 @@ %{_mandir}/man1/salt-call.1.gz %{_mandir}/man1/spm.1.gz %config(noreplace) %{_sysconfdir}/logrotate.d/salt -%if 0%{?suse_version} < 1500 -%doc LICENSE AUTHORS README.rst HACKING.rst README.SUSE -%else +%{!?_licensedir:%global license %doc} %license LICENSE %doc AUTHORS README.rst HACKING.rst README.SUSE -%endif # %dir %attr(0750, root, salt) %{_sysconfdir}/salt %dir %attr(0750, root, salt) %{_sysconfdir}/salt/pki @@ -1632,9 +1733,17 @@ %files standalone-formulas-configuration %defattr(-,root,root) +%dir %attr(0755, root, salt) %{_sysconfdir}/salt/master.d/ %config(noreplace) %attr(0640, root, salt) %{_sysconfdir}/salt/master.d/standalone-formulas-configuration.conf +%dir %attr(0750, root, root) %{_sysconfdir}/salt/minion.d/ +%config(noreplace) %attr(0640, root, root) %{_sysconfdir}/salt/minion.d/standalone-formulas-configuration.conf %dir %attr(0755, root, salt) %{_prefix}/share/salt-formulas/ %dir %attr(0755, root, salt) %{_prefix}/share/salt-formulas/states/ %dir %attr(0755, root, salt) %{_prefix}/share/salt-formulas/metadata/ +%files transactional-update +%defattr(-,root,root) +%config(noreplace) %attr(0640, root, root) %{_sysconfdir}/salt/minion.d/transactional_update.conf + + %changelog ++++++ _lastrevision ++++++ --- /var/tmp/diff_new_pack.WWBdJC/_old 2021-09-20 23:33:02.939165854 +0200 +++ /var/tmp/diff_new_pack.WWBdJC/_new 2021-09-20 23:33:02.939165854 +0200 @@ -1 +1 @@ -a94708ad2eba9aa15413d989ab3361b2c980589e \ No newline at end of file +71392e10750f7481475066788a23a39ad92d0c64 \ No newline at end of file ++++++ add-missing-aarch64-to-rpm-package-architectures-405.patch ++++++ >From e7723f081cc79088156a986cf940349fec7f00a3 Mon Sep 17 00:00:00 2001 From: Victor Zhestkov <35733135+vzhest...@users.noreply.github.com> Date: Wed, 18 Aug 2021 15:05:42 +0300 Subject: [PATCH] Add missing aarch64 to rpm package architectures (#405) Required to prevent false negative results on using pkg.installed with architecture specification in package name (ex. `bash.aarch64`) --- salt/utils/pkg/rpm.py | 2 +- tests/unit/modules/test_zypperpkg.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/salt/utils/pkg/rpm.py b/salt/utils/pkg/rpm.py index d1b149ea0b..8b8ea2e4b1 100644 --- a/salt/utils/pkg/rpm.py +++ b/salt/utils/pkg/rpm.py @@ -33,7 +33,7 @@ ARCHES_ALPHA = ( "alphaev68", "alphaev7", ) -ARCHES_ARM = ("armv5tel", "armv5tejl", "armv6l", "armv7l") +ARCHES_ARM = ("armv5tel", "armv5tejl", "armv6l", "armv7l", "aarch64") ARCHES_SH = ("sh3", "sh4", "sh4a") ARCHES = ( diff --git a/tests/unit/modules/test_zypperpkg.py b/tests/unit/modules/test_zypperpkg.py index 5c01bbbfbd..d6a6a6d852 100644 --- a/tests/unit/modules/test_zypperpkg.py +++ b/tests/unit/modules/test_zypperpkg.py @@ -2477,3 +2477,23 @@ pattern() = package-c""" with patch("salt.modules.zypperpkg.__zypper__", zypper_mock): assert zypper.services_need_restart() == expected zypper_mock(root=None).nolock.call.assert_called_with("ps", "-sss") + + def test_normalize_name(self): + """ + Test that package is normalized only when it should be + """ + with patch.dict(zypper.__grains__, {"osarch": "x86_64"}): + result = zypper.normalize_name("foo") + assert result == "foo", result + result = zypper.normalize_name("foo.x86_64") + assert result == "foo", result + result = zypper.normalize_name("foo.noarch") + assert result == "foo", result + + with patch.dict(zypper.__grains__, {"osarch": "aarch64"}): + result = zypper.normalize_name("foo") + assert result == "foo", result + result = zypper.normalize_name("foo.aarch64") + assert result == "foo", result + result = zypper.normalize_name("foo.noarch") + assert result == "foo", result -- 2.32.0 ++++++ adding-preliminary-support-for-rocky.-59682-391.patch ++++++ >From 34a913b0b54b55edf042dc899250e56ef0eaec77 Mon Sep 17 00:00:00 2001 From: Victor Zhestkov <35733135+vzhest...@users.noreply.github.com> Date: Mon, 5 Jul 2021 18:57:26 +0300 Subject: [PATCH] Adding preliminary support for Rocky. (#59682) (#391) * Adding preliminary support for Rocky. * Adding changelog and test per MR guidence. * Update test_core.py Fix a clean up issue Co-authored-by: Megan Wilhite <megan.wilh...@gmail.com> Co-authored-by: Gareth J. Greenaway <gar...@wiked.org> Co-authored-by: StackKorora <42156355+stackkor...@users.noreply.github.com> Co-authored-by: Megan Wilhite <megan.wilh...@gmail.com> Co-authored-by: Gareth J. Greenaway <gar...@wiked.org> --- changelog/59682.added | 1 + salt/grains/core.py | 2 ++ tests/unit/grains/test_core.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 changelog/59682.added diff --git a/changelog/59682.added b/changelog/59682.added new file mode 100644 index 0000000000..93b4a3d1fc --- /dev/null +++ b/changelog/59682.added @@ -0,0 +1 @@ +Rocky Linux has been added to the RedHat os_family. diff --git a/salt/grains/core.py b/salt/grains/core.py index 2b965a2a8a..ace0e4bff9 100644 --- a/salt/grains/core.py +++ b/salt/grains/core.py @@ -1547,6 +1547,7 @@ _OS_NAME_MAP = { "slesexpand": "RES", "linuxmint": "Mint", "neon": "KDE neon", + "rocky": "Rocky", "alibabaclo": "Alinux", } @@ -1621,6 +1622,7 @@ _OS_FAMILY_MAP = { "Funtoo": "Gentoo", "AIX": "AIX", "TurnKey": "Debian", + "Rocky": "RedHat", "AstraLinuxCE": "Debian", "Alinux": "RedHat", } diff --git a/tests/unit/grains/test_core.py b/tests/unit/grains/test_core.py index 8280d6de47..61a6956e32 100644 --- a/tests/unit/grains/test_core.py +++ b/tests/unit/grains/test_core.py @@ -678,6 +678,35 @@ class CoreGrainsTestCase(TestCase, LoaderModuleMockMixin): } self._run_os_grains_tests(None, _os_release_map, expectation) + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") + def test_rocky_8_os_grains(self): + """ + Test if OS grains are parsed correctly in Rocky 8 + """ + _os_release_map = { + "os_release_file": { + "NAME": "Rocky", + "VERSION_ID": "8.3", + "PRETTY_NAME": "Rocky 8", + "ID": "Rocky", + "ANSI_COLOR": "0;31", + "CPE_NAME": "cpe:/o:rocky:rocky:8.3", + }, + "_linux_distribution": ("rocky", "8.3", ""), + } + + expectation = { + "os": "Rocky", + "os_family": "RedHat", + "oscodename": "Rocky 8", + "osfullname": "Rocky", + "osrelease": "8.3", + "osrelease_info": (8, 3,), + "osmajorrelease": 8, + "osfinger": "Rocky-8", + } + self._run_os_grains_tests(None, _os_release_map, expectation) + @skipIf(not salt.utils.platform.is_linux(), "System is not Linux") def test_almalinux_8_os_grains(self): """ -- 2.32.0 ++++++ backport-of-upstream-pr59492-to-3002.2-404.patch ++++++ >From fba6631e0a66a5f8ea76a104e9acf385ce06471c Mon Sep 17 00:00:00 2001 From: Victor Zhestkov <35733135+vzhest...@users.noreply.github.com> Date: Wed, 18 Aug 2021 15:05:30 +0300 Subject: [PATCH] Backport of upstream PR59492 to 3002.2 (#404) * Fix failing integration tests * Fix unless logic and failing tests * Revert some of the changes in the onlyif code Co-authored-by: twangboy <s...@saltstack.com> --- salt/state.py | 24 +++++++++------ .../files/file/base/issue-35384.sls | 7 +++++ tests/unit/test_state.py | 30 ++++++++++++++----- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/salt/state.py b/salt/state.py index 070a914636..64c5225728 100644 --- a/salt/state.py +++ b/salt/state.py @@ -929,7 +929,8 @@ class State: def _run_check_onlyif(self, low_data, cmd_opts): """ - Check that unless doesn't return 0, and that onlyif returns a 0. + Make sure that all commands return True for the state to run. If any + command returns False (non 0), the state will not run """ ret = {"result": False} @@ -938,7 +939,9 @@ class State: else: low_data_onlyif = low_data["onlyif"] + # If any are False the state will NOT run def _check_cmd(cmd): + # Don't run condition (False) if cmd != 0 and ret["result"] is False: ret.update( { @@ -1001,7 +1004,8 @@ class State: def _run_check_unless(self, low_data, cmd_opts): """ - Check that unless doesn't return 0, and that onlyif returns a 0. + Check if any of the commands return False (non 0). If any are False the + state will run. """ ret = {"result": False} @@ -1010,8 +1014,10 @@ class State: else: low_data_unless = low_data["unless"] + # If any are False the state will run def _check_cmd(cmd): - if cmd == 0 and ret["result"] is False: + # Don't run condition + if cmd == 0: ret.update( { "comment": "unless condition is true", @@ -1020,9 +1026,10 @@ class State: } ) return False - elif cmd != 0: + else: + ret.pop("skip_watch", None) ret.update({"comment": "unless condition is false", "result": False}) - return True + return True for entry in low_data_unless: if isinstance(entry, str): @@ -1034,7 +1041,7 @@ class State: except CommandExecutionError: # Command failed, so notify unless to skip the item cmd = 0 - if not _check_cmd(cmd): + if _check_cmd(cmd): return ret elif isinstance(entry, dict): if "fun" not in entry: @@ -1047,7 +1054,7 @@ class State: if get_return: result = salt.utils.data.traverse_dict_and_list(result, get_return) if self.state_con.get("retcode", 0): - if not _check_cmd(self.state_con["retcode"]): + if _check_cmd(self.state_con["retcode"]): return ret elif result: ret.update( @@ -1057,11 +1064,11 @@ class State: "result": True, } ) - return ret else: ret.update( {"comment": "unless condition is false", "result": False} ) + return ret else: ret.update( { @@ -1069,7 +1076,6 @@ class State: "result": False, } ) - return ret # No reason to stop, return ret return ret diff --git a/tests/integration/files/file/base/issue-35384.sls b/tests/integration/files/file/base/issue-35384.sls index 3c41617ca8..2aa436bb37 100644 --- a/tests/integration/files/file/base/issue-35384.sls +++ b/tests/integration/files/file/base/issue-35384.sls @@ -2,5 +2,12 @@ cmd_run_unless_multiple: cmd.run: - name: echo "hello" - unless: + {% if grains["os"] == "Windows" %} + - "exit 0" + - "exit 1" + - "exit 0" + {% else %} - "$(which true)" - "$(which false)" + - "$(which true)" + {% endif %} diff --git a/tests/unit/test_state.py b/tests/unit/test_state.py index 95018a9cf3..79a261d837 100644 --- a/tests/unit/test_state.py +++ b/tests/unit/test_state.py @@ -142,7 +142,7 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): def test_verify_onlyif_cmd_error(self): """ Simulates a failure in cmd.retcode from onlyif - This could occur is runas is specified with a user that does not exist + This could occur if runas is specified with a user that does not exist """ low_data = { "onlyif": "somecommand", @@ -175,7 +175,7 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): def test_verify_unless_cmd_error(self): """ Simulates a failure in cmd.retcode from unless - This could occur is runas is specified with a user that does not exist + This could occur if runas is specified with a user that does not exist """ low_data = { "unless": "somecommand", @@ -206,6 +206,10 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): self.assertEqual(expected_result, return_result) def test_verify_unless_list_cmd(self): + """ + If any of the unless commands return False (non 0) then the state should + run (no skip_watch). + """ low_data = { "state": "cmd", "name": 'echo "something"', @@ -217,9 +221,8 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): "fun": "run", } expected_result = { - "comment": "unless condition is true", - "result": True, - "skip_watch": True, + "comment": "unless condition is false", + "result": False, } with patch("salt.state.State._gather_pillar") as state_patch: minion_opts = self.get_temp_config("minion") @@ -228,6 +231,10 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): self.assertEqual(expected_result, return_result) def test_verify_unless_list_cmd_different_order(self): + """ + If any of the unless commands return False (non 0) then the state should + run (no skip_watch). The order shouldn't matter. + """ low_data = { "state": "cmd", "name": 'echo "something"', @@ -239,9 +246,8 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): "fun": "run", } expected_result = { - "comment": "unless condition is true", - "result": True, - "skip_watch": True, + "comment": "unless condition is false", + "result": False, } with patch("salt.state.State._gather_pillar") as state_patch: minion_opts = self.get_temp_config("minion") @@ -272,6 +278,10 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): self.assertEqual(expected_result, return_result) def test_verify_unless_list_cmd_valid(self): + """ + If any of the unless commands return False (non 0) then the state should + run (no skip_watch). This tests all commands return False. + """ low_data = { "state": "cmd", "name": 'echo "something"', @@ -308,6 +318,10 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): self.assertEqual(expected_result, return_result) def test_verify_unless_list_cmd_invalid(self): + """ + If any of the unless commands return False (non 0) then the state should + run (no skip_watch). This tests all commands return True + """ low_data = { "state": "cmd", "name": 'echo "something"', -- 2.32.0 ++++++ backport-thread.is_alive-fix-390.patch ++++++ >From a782af246a2f3d4b91afee2ee847c87f71e8904b Mon Sep 17 00:00:00 2001 From: Alexander Graul <agr...@suse.com> Date: Fri, 25 Jun 2021 13:34:38 +0200 Subject: [PATCH] Backport Thread.is_alive fix (#390) * Change thread.isAlive() to thread.is_alive() (cherry picked from commit b1dc0cee03896c8abad55a609805b0be6c7aaefa) * Run pre-commit on salt/utils/timed_subprocess.py (cherry picked from commit 178e3b83e6c21abf5d6db454c19c104ceb8bd92c) * Fix the six removal made by pre-commit (cherry picked from commit aaa8ca3b7f129568637799d6d49d7ad3708f73bc) * Remove the PY2 code in salt/utils/timed_subprocess.py (cherry picked from commit 3a702a510b965e9af1ad318c953e19114925357e) Co-authored-by: Petr Messner <p...@leadhub.co> Co-authored-by: Petr Messner <petr.mess...@gmail.com> --- salt/utils/timed_subprocess.py | 39 ++++++++++++++-------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/salt/utils/timed_subprocess.py b/salt/utils/timed_subprocess.py index 5c4ac35ac3..b043a3bde2 100644 --- a/salt/utils/timed_subprocess.py +++ b/salt/utils/timed_subprocess.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- """ For running command line executables with a timeout """ -from __future__ import absolute_import, print_function, unicode_literals import shlex import subprocess @@ -10,10 +8,9 @@ import threading import salt.exceptions import salt.utils.data -from salt.ext import six -class TimedProc(object): +class TimedProc: """ Create a TimedProc object, calls subprocess.Popen with passed args and **kwargs """ @@ -46,7 +43,7 @@ class TimedProc(object): if self.timeout and not isinstance(self.timeout, (int, float)): raise salt.exceptions.TimedProcTimeoutError( - "Error: timeout {0} must be a number".format(self.timeout) + "Error: timeout {} must be a number".format(self.timeout) ) if kwargs.get("shell", False): args = salt.utils.data.decode(args, to_str=True) @@ -59,28 +56,24 @@ class TimedProc(object): try: args = shlex.split(args) except AttributeError: - args = shlex.split(six.text_type(args)) + args = shlex.split(str(args)) str_args = [] for arg in args: - if not isinstance(arg, six.string_types): - str_args.append(six.text_type(arg)) + if not isinstance(arg, str): + str_args.append(str(arg)) else: str_args.append(arg) args = str_args else: - if not isinstance(args, (list, tuple, six.string_types)): + if not isinstance(args, (list, tuple, str)): # Handle corner case where someone does a 'cmd.run 3' - args = six.text_type(args) + args = str(args) # Ensure that environment variables are strings - for key, val in six.iteritems(kwargs.get("env", {})): - if not isinstance(val, six.string_types): - kwargs["env"][key] = six.text_type(val) - if not isinstance(key, six.string_types): - kwargs["env"][six.text_type(key)] = kwargs["env"].pop(key) - if six.PY2 and "env" in kwargs: - # Ensure no unicode in custom env dict, as it can cause - # problems with subprocess. - kwargs["env"] = salt.utils.data.encode_dict(kwargs["env"]) + for key, val in kwargs.get("env", {}).items(): + if not isinstance(val, str): + kwargs["env"][key] = str(val) + if not isinstance(key, str): + kwargs["env"][str(key)] = kwargs["env"].pop(key) args = salt.utils.data.decode(args) self.process = subprocess.Popen(args, **kwargs) self.command = args @@ -103,18 +96,18 @@ class TimedProc(object): rt = threading.Thread(target=receive) rt.start() rt.join(self.timeout) - if rt.isAlive(): + if rt.is_alive(): # Subprocess cleanup (best effort) self.process.kill() def terminate(): - if rt.isAlive(): + if rt.is_alive(): self.process.terminate() threading.Timer(10, terminate).start() raise salt.exceptions.TimedProcTimeoutError( - "{0} : Timed out after {1} seconds".format( - self.command, six.text_type(self.timeout), + "{} : Timed out after {} seconds".format( + self.command, str(self.timeout), ) ) return self.process.returncode -- 2.32.0 ++++++ better-handling-of-bad-public-keys-from-minions-bsc-.patch ++++++ >From cd64b9a063771829f85d6be0e42259825cfb10c8 Mon Sep 17 00:00:00 2001 From: "Daniel A. Wozniak" <dwozn...@saltstack.com> Date: Mon, 2 Aug 2021 13:50:37 -0700 Subject: [PATCH] Better handling of bad public keys from minions (bsc#1189040) Add changelog for #57733 Fix pre-commit check Add missing test Fix test on older pythons --- changelog/57733.fixed | 1 + salt/crypt.py | 11 ++++++-- salt/exceptions.py | 6 ++++ salt/key.py | 15 ++++++++-- salt/transport/mixins/auth.py | 12 ++++---- .../pytests/integration/cli/test_salt_key.py | 28 +++++++++++++++++++ tests/pytests/unit/test_crypt.py | 20 +++++++++++++ 7 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 changelog/57733.fixed create mode 100644 tests/pytests/unit/test_crypt.py diff --git a/changelog/57733.fixed b/changelog/57733.fixed new file mode 100644 index 0000000000..0cd55b19a6 --- /dev/null +++ b/changelog/57733.fixed @@ -0,0 +1 @@ +Better handling of bad RSA public keys from minions diff --git a/salt/crypt.py b/salt/crypt.py index 0a8b728f50..e6e4f3181e 100644 --- a/salt/crypt.py +++ b/salt/crypt.py @@ -36,6 +36,7 @@ import salt.utils.verify import salt.version from salt.exceptions import ( AuthenticationError, + InvalidKeyError, MasterExit, SaltClientError, SaltReqTimeoutError, @@ -217,10 +218,16 @@ def get_rsa_pub_key(path): with salt.utils.files.fopen(path, "rb") as f: data = f.read().replace(b"RSA ", b"") bio = BIO.MemoryBuffer(data) - key = RSA.load_pub_key_bio(bio) + try: + key = RSA.load_pub_key_bio(bio) + except RSA.RSAError: + raise InvalidKeyError("Encountered bad RSA public key") else: with salt.utils.files.fopen(path) as f: - key = RSA.importKey(f.read()) + try: + key = RSA.importKey(f.read()) + except (ValueError, IndexError, TypeError): + raise InvalidKeyError("Encountered bad RSA public key") return key diff --git a/salt/exceptions.py b/salt/exceptions.py index 033a19cc54..1da15f9e69 100644 --- a/salt/exceptions.py +++ b/salt/exceptions.py @@ -111,6 +111,12 @@ class AuthenticationError(SaltException): """ +class InvalidKeyError(SaltException): + """ + Raised when we encounter an invalid RSA key. + """ + + class CommandNotFoundError(SaltException): """ Used in modules or grains when a required binary is not available diff --git a/salt/key.py b/salt/key.py index 75777ede06..59090c979c 100644 --- a/salt/key.py +++ b/salt/key.py @@ -11,6 +11,7 @@ import fnmatch import logging import os import shutil +import sys # Import salt libs import salt.cache @@ -652,17 +653,27 @@ class Key(object): keydirs.append(self.REJ) if include_denied: keydirs.append(self.DEN) + invalid_keys = [] for keydir in keydirs: for key in matches.get(keydir, []): + key_path = os.path.join(self.opts["pki_dir"], keydir, key) + try: + salt.crypt.get_rsa_pub_key(key_path) + except salt.exceptions.InvalidKeyError: + log.error("Invalid RSA public key: %s", key) + invalid_keys.append((keydir, key)) + continue try: shutil.move( - os.path.join(self.opts["pki_dir"], keydir, key), - os.path.join(self.opts["pki_dir"], self.ACC, key), + key_path, os.path.join(self.opts["pki_dir"], self.ACC, key), ) eload = {"result": True, "act": "accept", "id": key} self.event.fire_event(eload, salt.utils.event.tagify(prefix="key")) except (IOError, OSError): pass + for keydir, key in invalid_keys: + matches[keydir].remove(key) + sys.stderr.write("Unable to accept invalid key for {}.\n".format(key)) return self.name_match(match) if match is not None else self.dict_match(matches) def accept_all(self): diff --git a/salt/transport/mixins/auth.py b/salt/transport/mixins/auth.py index 003cbd8275..0f0c615408 100644 --- a/salt/transport/mixins/auth.py +++ b/salt/transport/mixins/auth.py @@ -184,11 +184,11 @@ class AESReqServerMixin(object): tagged "auth" and returns a dict with information about the auth event - # Verify that the key we are receiving matches the stored key - # Store the key if it is not there - # Make an RSA key with the pub key - # Encrypt the AES key as an encrypted salt.payload - # Package the return and return it + - Verify that the key we are receiving matches the stored key + - Store the key if it is not there + - Make an RSA key with the pub key + - Encrypt the AES key as an encrypted salt.payload + - Package the return and return it """ if not salt.utils.verify.valid_id(self.opts, load["id"]): @@ -460,7 +460,7 @@ class AESReqServerMixin(object): # and an empty request comes in try: pub = salt.crypt.get_rsa_pub_key(pubfn) - except (ValueError, IndexError, TypeError) as err: + except salt.crypt.InvalidKeyError as err: log.error('Corrupt public key "%s": %s', pubfn, err) return {"enc": "clear", "load": {"ret": False}} diff --git a/tests/pytests/integration/cli/test_salt_key.py b/tests/pytests/integration/cli/test_salt_key.py index 0edb2cf86c..2583348ce6 100644 --- a/tests/pytests/integration/cli/test_salt_key.py +++ b/tests/pytests/integration/cli/test_salt_key.py @@ -328,3 +328,31 @@ def test_keys_generation_keysize_max(salt_key_cli): ) assert ret.exitcode != 0 assert "error: The maximum value for keysize is 32768" in ret.stderr + +def test_keys_generation_keysize_max(salt_key_cli, tmp_path): + ret = salt_key_cli.run( + "--gen-keys", "minibar", "--gen-keys-dir", str(tmp_path), "--keysize", "32769" + ) + assert ret.exitcode != 0 + assert "error: The maximum value for keysize is 32768" in ret.stderr + + +def test_accept_bad_key(salt_master, salt_key_cli): + """ + test salt-key -d usage + """ + min_name = random_string("minibar-") + pki_dir = salt_master.config["pki_dir"] + key = os.path.join(pki_dir, "minions_pre", min_name) + + with salt.utils.files.fopen(key, "w") as fp: + fp.write("") + + try: + # Check Key + ret = salt_key_cli.run("-y", "-a", min_name) + assert ret.exitcode == 0 + assert "invalid key for {}".format(min_name) in ret.stderr + finally: + if os.path.exists(key): + os.remove(key) diff --git a/tests/pytests/unit/test_crypt.py b/tests/pytests/unit/test_crypt.py new file mode 100644 index 0000000000..aa8f439b8c --- /dev/null +++ b/tests/pytests/unit/test_crypt.py @@ -0,0 +1,20 @@ +""" +tests.pytests.unit.test_crypt +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unit tests for salt's crypt module +""" +import pytest +import salt.crypt +import salt.utils.files + + +def test_get_rsa_pub_key_bad_key(tmp_path): + """ + get_rsa_pub_key raises InvalidKeyError when encoutering a bad key + """ + key_path = str(tmp_path / "key") + with salt.utils.files.fopen(key_path, "w") as fp: + fp.write("") + with pytest.raises(salt.crypt.InvalidKeyError): + salt.crypt.get_rsa_pub_key(key_path) -- 2.32.0 ++++++ check-if-dpkgnotify-is-executable-bsc-1186674-376.patch ++++++ >From b477b00447b49fc2f221cfb6d2c491bcd1970119 Mon Sep 17 00:00:00 2001 From: Victor Zhestkov <35733135+vzhest...@users.noreply.github.com> Date: Tue, 1 Jun 2021 13:04:43 +0300 Subject: [PATCH] Check if dpkgnotify is executable (bsc#1186674) (#376) It prevents fails on removing salt-minion package when the dpkg configuration is still active --- scripts/suse/dpkg/99dpkgnotify | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/suse/dpkg/99dpkgnotify b/scripts/suse/dpkg/99dpkgnotify index 8013387a57..f89815f605 100644 --- a/scripts/suse/dpkg/99dpkgnotify +++ b/scripts/suse/dpkg/99dpkgnotify @@ -1 +1 @@ -DPkg::Post-Invoke {"/usr/bin/dpkgnotify";}; +DPkg::Post-Invoke {"if [ -x /usr/bin/dpkgnotify ]; then /usr/bin/dpkgnotify; fi;";}; -- 2.31.1 ++++++ do-noop-for-services-states-when-running-systemd-in-.patch ++++++ ++++ 631 lines (skipped) ++++++ don-t-use-shell-sbin-nologin-in-requisites.patch ++++++ >From 9a8ca020a3cacbcfbbc33f209cd0ea6c3da3f788 Mon Sep 17 00:00:00 2001 From: Alexander Graul <agr...@suse.com> Date: Tue, 17 Aug 2021 11:52:00 +0200 Subject: [PATCH] Don't use shell="/sbin/nologin" in requisites Using shell="/sbin/nologin" in an onlyif/unless requisite does not really make sense since the condition can't be run. shell=/sbin/nologin is also a common argument, e.g. for user.present. Fixes: bsc#1188259 --- salt/state.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/salt/state.py b/salt/state.py index 64c5225728..c6742101b2 100644 --- a/salt/state.py +++ b/salt/state.py @@ -889,9 +889,14 @@ class State: cmd_opts[run_cmd_arg] = low_data.get(run_cmd_arg) if "shell" in low_data: - cmd_opts["shell"] = low_data["shell"] + shell = low_data["shell"] elif "shell" in self.opts["grains"]: - cmd_opts["shell"] = self.opts["grains"].get("shell") + shell = self.opts["grains"].get("shell") + else: + shell = None + # /sbin/nologin always causes the onlyif / unless cmd to fail + if shell is not None and shell != "/sbin/nologin": + cmd_opts["shell"] = shell if "onlyif" in low_data: _ret = self._run_check_onlyif(low_data, cmd_opts) -- 2.32.0 ++++++ enhance-logging-when-inotify-beacon-is-missing-pyino.patch ++++++ >From cde0f9385e1afb9fa97fe2c86cfa77ae3b899aa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= <psuarezhernan...@suse.com> Date: Fri, 18 Jun 2021 13:09:22 +0100 Subject: [PATCH] Enhance logging when inotify beacon is missing pyinotify (bsc#1186310) --- salt/beacons/inotify.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/salt/beacons/inotify.py b/salt/beacons/inotify.py index fa2f73c35f..a6b7548f97 100644 --- a/salt/beacons/inotify.py +++ b/salt/beacons/inotify.py @@ -49,7 +49,9 @@ log = logging.getLogger(__name__) def __virtual__(): if HAS_PYINOTIFY: return __virtualname__ - return False + err_msg = "pyinotify library is missing" + log.error("Unable to load inotify beacon: {}".format(err_msg)) + return False, err_msg def _get_mask(mask): -- 2.31.1 ++++++ enhance-openscap-module-add-xccdf_eval-call-386.patch ++++++ >From 1fd51c17bc03e679a040f2c6d9ac107a2c57b7c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= <psuarezhernan...@suse.com> Date: Wed, 7 Jul 2021 15:41:48 +0100 Subject: [PATCH] Enhance openscap module: add "xccdf_eval" call (#386) * Enhance openscap module: add xccdf_eval call * Allow 'tailoring_file' and 'tailoring_id' parameters * Fix wrong reference to subprocess.PIPE in openscap unit tests * Add changes suggested by pre-commit Co-authored-by: Michael Calmer <m...@suse.de> --- changelog/59756.added | 1 + salt/modules/openscap.py | 120 ++++++++++++- tests/unit/modules/test_openscap.py | 262 +++++++++++++++++++++++++--- 3 files changed, 353 insertions(+), 30 deletions(-) create mode 100644 changelog/59756.added diff --git a/changelog/59756.added b/changelog/59756.added new file mode 100644 index 0000000000..a59fb21eef --- /dev/null +++ b/changelog/59756.added @@ -0,0 +1 @@ +adding new call for openscap xccdf eval supporting new parameters diff --git a/salt/modules/openscap.py b/salt/modules/openscap.py index 6f8ff4a76d..f75e1c5e6b 100644 --- a/salt/modules/openscap.py +++ b/salt/modules/openscap.py @@ -1,20 +1,15 @@ -# -*- coding: utf-8 -*- """ Module for OpenSCAP Management """ -# Import Python libs -from __future__ import absolute_import, print_function, unicode_literals +import os.path import shlex import shutil import tempfile from subprocess import PIPE, Popen -# Import Salt libs -from salt.ext import six - ArgumentParser = object try: @@ -44,7 +39,7 @@ def __virtual__(): class _ArgumentParser(ArgumentParser): def __init__(self, action=None, *args, **kwargs): - super(_ArgumentParser, self).__init__(*args, prog="oscap", **kwargs) + super().__init__(*args, prog="oscap", **kwargs) self.add_argument("action", choices=["eval"]) add_arg = None for params, kwparams in _XCCDF_MAP["eval"]["parser_arguments"]: @@ -61,6 +56,115 @@ _OSCAP_EXIT_CODES_MAP = { } +def xccdf_eval(xccdffile, ovalfiles=None, **kwargs): + """ + Run ``oscap xccdf eval`` commands on minions. + It uses cp.push_dir to upload the generated files to the salt master + in the master's minion files cachedir + (defaults to ``/var/cache/salt/master/minions/minion-id/files``) + + It needs ``file_recv`` set to ``True`` in the master configuration file. + + xccdffile + the path to the xccdf file to evaluate + + ovalfiles + additional oval definition files + + profile + the name of Profile to be evaluated + + rule + the name of a single rule to be evaluated + + oval_results + save OVAL results as well (True or False) + + results + write XCCDF Results into given file + + report + write HTML report into given file + + fetch_remote_resources + download remote content referenced by XCCDF (True or False) + + tailoring_file + use given XCCDF Tailoring file + + tailoring_id + use given DS component as XCCDF Tailoring file + + remediate + automatically execute XCCDF fix elements for failed rules. + Use of this option is always at your own risk. (True or False) + + CLI Example: + + .. code-block:: bash + + salt '*' openscap.xccdf_eval /usr/share/openscap/scap-yast2sec-xccdf.xml profile=Default + + """ + success = True + error = None + upload_dir = None + returncode = None + if not ovalfiles: + ovalfiles = [] + + cmd_opts = ["oscap", "xccdf", "eval"] + if kwargs.get("oval_results"): + cmd_opts.append("--oval-results") + if "results" in kwargs: + cmd_opts.append("--results") + cmd_opts.append(kwargs["results"]) + if "report" in kwargs: + cmd_opts.append("--report") + cmd_opts.append(kwargs["report"]) + if "profile" in kwargs: + cmd_opts.append("--profile") + cmd_opts.append(kwargs["profile"]) + if "rule" in kwargs: + cmd_opts.append("--rule") + cmd_opts.append(kwargs["rule"]) + if "tailoring_file" in kwargs: + cmd_opts.append("--tailoring-file") + cmd_opts.append(kwargs["tailoring_file"]) + if "tailoring_id" in kwargs: + cmd_opts.append("--tailoring-id") + cmd_opts.append(kwargs["tailoring_id"]) + if kwargs.get("fetch_remote_resources"): + cmd_opts.append("--fetch-remote-resources") + if kwargs.get("remediate"): + cmd_opts.append("--remediate") + cmd_opts.append(xccdffile) + cmd_opts.extend(ovalfiles) + + if not os.path.exists(xccdffile): + success = False + error = "XCCDF File '{}' does not exist".format(xccdffile) + for ofile in ovalfiles: + if success and not os.path.exists(ofile): + success = False + error = "Oval File '{}' does not exist".format(ofile) + + if success: + tempdir = tempfile.mkdtemp() + proc = Popen(cmd_opts, stdout=PIPE, stderr=PIPE, cwd=tempdir) + (stdoutdata, error) = proc.communicate() + success = _OSCAP_EXIT_CODES_MAP[proc.returncode] + returncode = proc.returncode + if success: + __salt__["cp.push_dir"](tempdir) + upload_dir = tempdir + shutil.rmtree(tempdir, ignore_errors=True) + + return dict( + success=success, upload_dir=upload_dir, error=error, returncode=returncode + ) + + def xccdf(params): """ Run ``oscap xccdf`` commands on minions. @@ -91,7 +195,7 @@ def xccdf(params): args, argv = _ArgumentParser(action=action).parse_known_args(args=params) except Exception as err: # pylint: disable=broad-except success = False - error = six.text_type(err) + error = str(err) if success: cmd = _XCCDF_MAP[action]["cmd_pattern"].format(args.profile, policy) diff --git a/tests/unit/modules/test_openscap.py b/tests/unit/modules/test_openscap.py index 04cf00a1d3..e5be151bf2 100644 --- a/tests/unit/modules/test_openscap.py +++ b/tests/unit/modules/test_openscap.py @@ -1,18 +1,8 @@ -# -*- coding: utf-8 -*- - -# Import python libs -from __future__ import absolute_import, print_function, unicode_literals - from subprocess import PIPE -# Import salt libs import salt.modules.openscap as openscap - -# Import 3rd-party libs from salt.ext import six from tests.support.mock import MagicMock, Mock, patch - -# Import salt test libs from tests.support.unit import TestCase @@ -32,6 +22,7 @@ class OpenscapTestCase(TestCase): "salt.modules.openscap.tempfile.mkdtemp", Mock(return_value=self.random_temp_dir), ), + patch("salt.modules.openscap.os.path.exists", Mock(return_value=True)), ] for patcher in patchers: self.apply_patch(patcher) @@ -50,7 +41,7 @@ class OpenscapTestCase(TestCase): ), ): response = openscap.xccdf( - "eval --profile Default {0}".format(self.policy_file) + "eval --profile Default {}".format(self.policy_file) ) self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1) @@ -97,7 +88,7 @@ class OpenscapTestCase(TestCase): ), ): response = openscap.xccdf( - "eval --profile Default {0}".format(self.policy_file) + "eval --profile Default {}".format(self.policy_file) ) self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1) @@ -136,10 +127,7 @@ class OpenscapTestCase(TestCase): def test_openscap_xccdf_eval_fail_no_profile(self): response = openscap.xccdf("eval --param Default /unknown/param") - if six.PY2: - error = "argument --profile is required" - else: - error = "the following arguments are required: --profile" + error = "the following arguments are required: --profile" self.assertEqual( response, {"error": error, "upload_dir": None, "success": False, "returncode": None}, @@ -199,7 +187,7 @@ class OpenscapTestCase(TestCase): ), ): response = openscap.xccdf( - "eval --profile Default {0}".format(self.policy_file) + "eval --profile Default {}".format(self.policy_file) ) self.assertEqual( @@ -213,11 +201,8 @@ class OpenscapTestCase(TestCase): ) def test_openscap_xccdf_eval_fail_not_implemented_action(self): - response = openscap.xccdf("info {0}".format(self.policy_file)) - if six.PY2: - mock_err = "argument action: invalid choice: 'info' (choose from u'eval')" - else: - mock_err = "argument action: invalid choice: 'info' (choose from 'eval')" + response = openscap.xccdf("info {}".format(self.policy_file)) + mock_err = "argument action: invalid choice: 'info' (choose from 'eval')" self.assertEqual( response, @@ -228,3 +213,236 @@ class OpenscapTestCase(TestCase): "returncode": None, }, ) + + def test_new_openscap_xccdf_eval_success(self): + with patch( + "salt.modules.openscap.Popen", + MagicMock( + return_value=Mock( + **{"returncode": 0, "communicate.return_value": ("", "")} + ) + ), + ): + response = openscap.xccdf_eval( + self.policy_file, + profile="Default", + oval_results=True, + results="results.xml", + report="report.html", + ) + + self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1) + expected_cmd = [ + "oscap", + "xccdf", + "eval", + "--oval-results", + "--results", + "results.xml", + "--report", + "report.html", + "--profile", + "Default", + self.policy_file, + ] + openscap.Popen.assert_called_once_with( + expected_cmd, + cwd=openscap.tempfile.mkdtemp.return_value, + stderr=PIPE, + stdout=PIPE, + ) + openscap.__salt__["cp.push_dir"].assert_called_once_with( + self.random_temp_dir + ) + self.assertEqual(openscap.shutil.rmtree.call_count, 1) + self.assertEqual( + response, + { + "upload_dir": self.random_temp_dir, + "error": "", + "success": True, + "returncode": 0, + }, + ) + + def test_new_openscap_xccdf_eval_success_with_extra_ovalfiles(self): + with patch( + "salt.modules.openscap.Popen", + MagicMock( + return_value=Mock( + **{"returncode": 0, "communicate.return_value": ("", "")} + ) + ), + ): + response = openscap.xccdf_eval( + self.policy_file, + ["/usr/share/xml/another-oval.xml", "/usr/share/xml/oval.xml"], + profile="Default", + oval_results=True, + results="results.xml", + report="report.html", + ) + + self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1) + expected_cmd = [ + "oscap", + "xccdf", + "eval", + "--oval-results", + "--results", + "results.xml", + "--report", + "report.html", + "--profile", + "Default", + self.policy_file, + "/usr/share/xml/another-oval.xml", + "/usr/share/xml/oval.xml", + ] + openscap.Popen.assert_called_once_with( + expected_cmd, + cwd=openscap.tempfile.mkdtemp.return_value, + stderr=PIPE, + stdout=PIPE, + ) + openscap.__salt__["cp.push_dir"].assert_called_once_with( + self.random_temp_dir + ) + self.assertEqual(openscap.shutil.rmtree.call_count, 1) + self.assertEqual( + response, + { + "upload_dir": self.random_temp_dir, + "error": "", + "success": True, + "returncode": 0, + }, + ) + + def test_new_openscap_xccdf_eval_success_with_failing_rules(self): + with patch( + "salt.modules.openscap.Popen", + MagicMock( + return_value=Mock( + **{"returncode": 2, "communicate.return_value": ("", "some error")} + ) + ), + ): + response = openscap.xccdf_eval( + self.policy_file, + profile="Default", + oval_results=True, + results="results.xml", + report="report.html", + ) + + self.assertEqual(openscap.tempfile.mkdtemp.call_count, 1) + expected_cmd = [ + "oscap", + "xccdf", + "eval", + "--oval-results", + "--results", + "results.xml", + "--report", + "report.html", + "--profile", + "Default", + self.policy_file, + ] + openscap.Popen.assert_called_once_with( + expected_cmd, + cwd=openscap.tempfile.mkdtemp.return_value, + stderr=PIPE, + stdout=PIPE, + ) + openscap.__salt__["cp.push_dir"].assert_called_once_with( + self.random_temp_dir + ) + self.assertEqual(openscap.shutil.rmtree.call_count, 1) + self.assertEqual( + response, + { + "upload_dir": self.random_temp_dir, + "error": "some error", + "success": True, + "returncode": 2, + }, + ) + + def test_new_openscap_xccdf_eval_success_ignore_unknown_params(self): + with patch( + "salt.modules.openscap.Popen", + MagicMock( + return_value=Mock( + **{"returncode": 2, "communicate.return_value": ("", "some error")} + ) + ), + ): + response = openscap.xccdf_eval( + "/policy/file", + param="Default", + profile="Default", + oval_results=True, + results="results.xml", + report="report.html", + ) + + self.assertEqual( + response, + { + "upload_dir": self.random_temp_dir, + "error": "some error", + "success": True, + "returncode": 2, + }, + ) + expected_cmd = [ + "oscap", + "xccdf", + "eval", + "--oval-results", + "--results", + "results.xml", + "--report", + "report.html", + "--profile", + "Default", + "/policy/file", + ] + openscap.Popen.assert_called_once_with( + expected_cmd, + cwd=openscap.tempfile.mkdtemp.return_value, + stderr=PIPE, + stdout=PIPE, + ) + + def test_new_openscap_xccdf_eval_evaluation_error(self): + with patch( + "salt.modules.openscap.Popen", + MagicMock( + return_value=Mock( + **{ + "returncode": 1, + "communicate.return_value": ("", "evaluation error"), + } + ) + ), + ): + response = openscap.xccdf_eval( + self.policy_file, + profile="Default", + oval_results=True, + results="results.xml", + report="report.html", + ) + + self.assertEqual( + response, + { + "upload_dir": None, + "error": "evaluation error", + "success": False, + "returncode": 1, + }, + ) -- 2.32.0 ++++++ exclude-the-full-path-of-a-download-url-to-prevent-i.patch ++++++ >From 57ed9c41a177f57e3d56465662750617ac36cc95 Mon Sep 17 00:00:00 2001 From: Joe Eacott <jeac...@vmware.com> Date: Mon, 28 Jun 2021 16:46:35 -0600 Subject: [PATCH] Exclude the full path of a download URL to prevent injection of malicious code (bsc#1190265) (CVE-2021-21996) --- salt/fileclient.py | 7 +++++++ tests/unit/test_fileclient.py | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/salt/fileclient.py b/salt/fileclient.py index 88dcf1668d..bdf450ffe6 100644 --- a/salt/fileclient.py +++ b/salt/fileclient.py @@ -28,6 +28,7 @@ import salt.utils.platform import salt.utils.stringutils import salt.utils.templates import salt.utils.url +import salt.utils.verify import salt.utils.versions from salt.exceptions import CommandExecutionError, MinionError @@ -858,6 +859,12 @@ class Client: else: file_name = url_data.path + # clean_path returns an empty string if the check fails + root_path = salt.utils.path.join(cachedir, "extrn_files", saltenv, netloc) + new_path = os.path.sep.join([root_path, file_name]) + if not salt.utils.verify.clean_path(root_path, new_path, subdir=True): + return "Invalid path" + if len(file_name) > MAX_FILENAME_LENGTH: file_name = salt.utils.hashutils.sha256_digest(file_name) diff --git a/tests/unit/test_fileclient.py b/tests/unit/test_fileclient.py index 3aa7b7cf84..b6cc84a871 100644 --- a/tests/unit/test_fileclient.py +++ b/tests/unit/test_fileclient.py @@ -63,6 +63,24 @@ class FileclientTestCase(TestCase): ) as c_ref_itr: assert c_ref_itr == "/__test__/files/base/testfile" + def test_cache_extrn_path_valid(self): + """ + Tests for extrn_filepath for a given url + """ + file_name = "http://localhost:8000/test/location/src/dev/usr/file" + + ret = fileclient.Client(self.opts)._extrn_path(file_name, "base") + assert ret == os.path.join("__test__", "extrn_files", "base", ret) + + def test_cache_extrn_path_invalid(self): + """ + Tests for extrn_filepath for a given url + """ + file_name = "http://localhost:8000/../../../../../usr/bin/bad" + + ret = fileclient.Client(self.opts)._extrn_path(file_name, "base") + assert ret == "Invalid path" + def test_extrn_path_with_long_filename(self): safe_file_name = os.path.split( fileclient.Client(self.opts)._extrn_path( -- 2.33.0 ++++++ figure-out-python-interpreter-to-use-inside-containe.patch ++++++ >From 271826b0baa6b2281bc2eac9118a0fcc4675f106 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= <psuarezhernan...@suse.com> Date: Wed, 19 May 2021 16:24:27 +0100 Subject: [PATCH] Figure out Python interpreter to use inside containers Fix unit test for dockermod.call function --- salt/modules/dockermod.py | 28 +++++++++++++++++++++++--- tests/unit/modules/test_dockermod.py | 30 +++++++++++++++------------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/salt/modules/dockermod.py b/salt/modules/dockermod.py index 176b4db926..cad307e7af 100644 --- a/salt/modules/dockermod.py +++ b/salt/modules/dockermod.py @@ -217,7 +217,6 @@ import re import shutil import string import subprocess -import sys import time import uuid @@ -6865,9 +6864,32 @@ def call(name, function, *args, **kwargs): name, thin_path, os.path.join(thin_dest_path, os.path.basename(thin_path)) ) + # figure out available python interpreter inside the container + pycmds = ( + "python3", + "/usr/libexec/platform-python", + "python27", + "python2.7", + "python26", + "python2.6", + "python2", + "python", + ) + container_python_bin = None + for py_cmd in pycmds: + cmd = [py_cmd] + ["--version"] + ret = run_all(name, subprocess.list2cmdline(cmd)) + if ret["retcode"] == 0: + container_python_bin = py_cmd + break + if not container_python_bin: + raise CommandExecutionError( + "Python interpreter cannot be found inside the container. Make sure Python is installed in the container" + ) + # untar archive untar_cmd = [ - "python", + container_python_bin, "-c", ("import tarfile; " 'tarfile.open("{0}/{1}").extractall(path="{0}")').format( thin_dest_path, os.path.basename(thin_path) @@ -6880,7 +6902,7 @@ def call(name, function, *args, **kwargs): try: salt_argv = ( [ - "python{0}".format(sys.version_info[0]), + container_python_bin, os.path.join(thin_dest_path, "salt-call"), "--metadata", "--local", diff --git a/tests/unit/modules/test_dockermod.py b/tests/unit/modules/test_dockermod.py index 48526acb71..ebe97a83f5 100644 --- a/tests/unit/modules/test_dockermod.py +++ b/tests/unit/modules/test_dockermod.py @@ -1049,33 +1049,35 @@ class DockerTestCase(TestCase, LoaderModuleMockMixin): # [ call(name, [args]), ... self.maxDiff = None self.assertIn("mkdir", docker_run_all_mock.mock_calls[0][1][1]) - self.assertIn("mkdir", docker_run_all_mock.mock_calls[4][1][1]) + self.assertIn("mkdir", docker_run_all_mock.mock_calls[5][1][1]) self.assertNotEqual( docker_run_all_mock.mock_calls[0][1][1], - docker_run_all_mock.mock_calls[4][1][1], + docker_run_all_mock.mock_calls[5][1][1], ) - self.assertIn("salt-call", docker_run_all_mock.mock_calls[2][1][1]) - self.assertIn("salt-call", docker_run_all_mock.mock_calls[6][1][1]) + self.assertEqual("python3 --version", docker_run_all_mock.mock_calls[1][1][1]) + + self.assertIn("salt-call", docker_run_all_mock.mock_calls[3][1][1]) + self.assertIn("salt-call", docker_run_all_mock.mock_calls[8][1][1]) self.assertNotEqual( - docker_run_all_mock.mock_calls[2][1][1], - docker_run_all_mock.mock_calls[6][1][1], + docker_run_all_mock.mock_calls[3][1][1], + docker_run_all_mock.mock_calls[8][1][1], ) # check thin untar - self.assertIn("tarfile", docker_run_all_mock.mock_calls[1][1][1]) - self.assertIn("tarfile", docker_run_all_mock.mock_calls[5][1][1]) + self.assertIn("tarfile", docker_run_all_mock.mock_calls[2][1][1]) + self.assertIn("tarfile", docker_run_all_mock.mock_calls[7][1][1]) self.assertNotEqual( - docker_run_all_mock.mock_calls[1][1][1], - docker_run_all_mock.mock_calls[5][1][1], + docker_run_all_mock.mock_calls[2][1][1], + docker_run_all_mock.mock_calls[7][1][1], ) # check directory cleanup - self.assertIn("rm -rf", docker_run_all_mock.mock_calls[3][1][1]) - self.assertIn("rm -rf", docker_run_all_mock.mock_calls[7][1][1]) + self.assertIn("rm -rf", docker_run_all_mock.mock_calls[4][1][1]) + self.assertIn("rm -rf", docker_run_all_mock.mock_calls[9][1][1]) self.assertNotEqual( - docker_run_all_mock.mock_calls[3][1][1], - docker_run_all_mock.mock_calls[7][1][1], + docker_run_all_mock.mock_calls[4][1][1], + docker_run_all_mock.mock_calls[9][1][1], ) self.assertEqual({"retcode": 0, "comment": "container cmd"}, ret) -- 2.31.1 ++++++ fix-error-handling-in-openscap-module-bsc-1188647-40.patch ++++++ >From b7d11d8caf3eb4fb39a070201be87bb1b3abd525 Mon Sep 17 00:00:00 2001 From: Vladimir Nadvornik <nadvor...@suse.cz> Date: Wed, 11 Aug 2021 12:19:09 +0200 Subject: [PATCH] Fix error handling in openscap module (bsc#1188647) (#409) --- salt/modules/openscap.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/salt/modules/openscap.py b/salt/modules/openscap.py index f75e1c5e6b..216fd89eef 100644 --- a/salt/modules/openscap.py +++ b/salt/modules/openscap.py @@ -153,7 +153,9 @@ def xccdf_eval(xccdffile, ovalfiles=None, **kwargs): tempdir = tempfile.mkdtemp() proc = Popen(cmd_opts, stdout=PIPE, stderr=PIPE, cwd=tempdir) (stdoutdata, error) = proc.communicate() - success = _OSCAP_EXIT_CODES_MAP[proc.returncode] + success = _OSCAP_EXIT_CODES_MAP.get(proc.returncode, False) + if proc.returncode < 0: + error += "\nKilled by signal {}\n".format(proc.returncode).encode('ascii') returncode = proc.returncode if success: __salt__["cp.push_dir"](tempdir) @@ -202,7 +204,9 @@ def xccdf(params): tempdir = tempfile.mkdtemp() proc = Popen(shlex.split(cmd), stdout=PIPE, stderr=PIPE, cwd=tempdir) (stdoutdata, error) = proc.communicate() - success = _OSCAP_EXIT_CODES_MAP[proc.returncode] + success = _OSCAP_EXIT_CODES_MAP.get(proc.returncode, False) + if proc.returncode < 0: + error += "\nKilled by signal {}\n".format(proc.returncode).encode('ascii') returncode = proc.returncode if success: __salt__["cp.push_dir"](tempdir) -- 2.32.0 ++++++ fix-exception-in-yumpkg.remove-for-not-installed-pac.patch ++++++ >From 30a2c8c042f0fe57253a8ab47220d897bc89bd17 Mon Sep 17 00:00:00 2001 From: Victor Zhestkov <35733135+vzhest...@users.noreply.github.com> Date: Thu, 24 Jun 2021 13:17:13 +0300 Subject: [PATCH] Fix exception in yumpkg.remove for not installed package (#380) --- salt/modules/yumpkg.py | 2 ++ tests/unit/modules/test_yumpkg.py | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py index 0fb41a0400..c9f9f2c2d3 100644 --- a/salt/modules/yumpkg.py +++ b/salt/modules/yumpkg.py @@ -2051,6 +2051,8 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 old = list_pkgs() targets = [] for target in pkg_params: + if target not in old: + continue version_to_remove = pkg_params[target] installed_versions = old[target].split(",") diff --git a/tests/unit/modules/test_yumpkg.py b/tests/unit/modules/test_yumpkg.py index e22c0b9251..373d2e09cb 100644 --- a/tests/unit/modules/test_yumpkg.py +++ b/tests/unit/modules/test_yumpkg.py @@ -1099,6 +1099,31 @@ class YumTestCase(TestCase, LoaderModuleMockMixin): call = cmd_mock.mock_calls[0][1][0] assert call == expected, call + def test_remove_not_existing(self): + """ + Test if no exception on removing not installed package + """ + name = "foo" + def list_pkgs_mock(): + return {} + cmd_mock = MagicMock( + return_value={"pid": 12345, "retcode": 0, "stdout": "", "stderr": ""} + ) + salt_mock = { + "cmd.run_all": cmd_mock, + "lowpkg.version_cmp": rpm.version_cmp, + "pkg_resource.parse_targets": MagicMock( + return_value=({name: None}, "repository") + ), + } + with patch.object(yumpkg, "list_pkgs", list_pkgs_mock), patch( + "salt.utils.systemd.has_scope", MagicMock(return_value=False) + ), patch.dict(yumpkg.__salt__, salt_mock): + + with patch.dict(yumpkg.__grains__, {"os": "CentOS", "osrelease": 7}): + yumpkg.remove(name) + cmd_mock.assert_not_called() + def test_install_with_epoch(self): """ Tests that we properly identify a version containing an epoch as an -- 2.32.0 ++++++ fix-failing-unit-tests-for-systemd.patch ++++++ >From 74d8f5f2d896e5e8bbf7d3fb614ae32f2cf489a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= <psuarezhernan...@suse.com> Date: Wed, 11 Aug 2021 11:44:54 +0100 Subject: [PATCH] Fix failing unit tests for systemd --- tests/unit/modules/test_systemd_service.py | 24 ++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/unit/modules/test_systemd_service.py b/tests/unit/modules/test_systemd_service.py index bbd89bb3d0..51be130d29 100644 --- a/tests/unit/modules/test_systemd_service.py +++ b/tests/unit/modules/test_systemd_service.py @@ -165,21 +165,27 @@ class SystemdTestCase(TestCase, LoaderModuleMockMixin): # systemd < 231 with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 230}): - with patch.object(systemd, "_systemctl_status", mock): + with patch.object(systemd, "_systemctl_status", mock), patch.object( + systemd, "offline", MagicMock(return_value=False) + ): self.assertTrue(systemd.available("sshd.service")) self.assertFalse(systemd.available("foo.service")) # systemd >= 231 with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 231}): with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231): - with patch.object(systemd, "_systemctl_status", mock): + with patch.object(systemd, "_systemctl_status", mock), patch.object( + systemd, "offline", MagicMock(return_value=False) + ): self.assertTrue(systemd.available("sshd.service")) self.assertFalse(systemd.available("bar.service")) # systemd < 231 with retcode/output changes backported (e.g. RHEL 7.3) with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 219}): with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231): - with patch.object(systemd, "_systemctl_status", mock): + with patch.object(systemd, "_systemctl_status", mock), patch.object( + systemd, "offline", MagicMock(return_value=False) + ): self.assertTrue(systemd.available("sshd.service")) self.assertFalse(systemd.available("bar.service")) @@ -191,21 +197,27 @@ class SystemdTestCase(TestCase, LoaderModuleMockMixin): # systemd < 231 with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 230}): - with patch.object(systemd, "_systemctl_status", mock): + with patch.object(systemd, "_systemctl_status", mock), patch.object( + systemd, "offline", MagicMock(return_value=False) + ): self.assertFalse(systemd.missing("sshd.service")) self.assertTrue(systemd.missing("foo.service")) # systemd >= 231 with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 231}): with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231): - with patch.object(systemd, "_systemctl_status", mock): + with patch.object(systemd, "_systemctl_status", mock), patch.object( + systemd, "offline", MagicMock(return_value=False) + ): self.assertFalse(systemd.missing("sshd.service")) self.assertTrue(systemd.missing("bar.service")) # systemd < 231 with retcode/output changes backported (e.g. RHEL 7.3) with patch.dict(systemd.__context__, {"salt.utils.systemd.version": 219}): with patch.dict(_SYSTEMCTL_STATUS, _SYSTEMCTL_STATUS_GTE_231): - with patch.object(systemd, "_systemctl_status", mock): + with patch.object(systemd, "_systemctl_status", mock), patch.object( + systemd, "offline", MagicMock(return_value=False) + ): self.assertFalse(systemd.missing("sshd.service")) self.assertTrue(systemd.missing("bar.service")) -- 2.32.0 ++++++ fix-issue-parsing-errors-in-ansiblegate-state-module.patch ++++++ >From cc017f6ed279af7fe02c890e4a7725e6903f364d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= <psuarezhernan...@suse.com> Date: Mon, 26 Apr 2021 12:13:59 +0100 Subject: [PATCH] Fix issue parsing errors in ansiblegate state module --- salt/states/ansiblegate.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/salt/states/ansiblegate.py b/salt/states/ansiblegate.py index 5daba0f37f..bd00653928 100644 --- a/salt/states/ansiblegate.py +++ b/salt/states/ansiblegate.py @@ -183,7 +183,11 @@ def playbooks(name, rundir=None, git_repo=None, git_kwargs=None, ansible_kwargs= checks = __salt__["ansible.playbooks"]( name, rundir=rundir, check=True, diff=True, **ansible_kwargs ) - if all( + if "stats" not in checks: + ret["comment"] = checks.get("stderr", checks) + ret["result"] = False + ret["changes"] = {} + elif all( not check["changed"] and not check["failures"] and not check["unreachable"] @@ -212,7 +216,11 @@ def playbooks(name, rundir=None, git_repo=None, git_kwargs=None, ansible_kwargs= results = __salt__["ansible.playbooks"]( name, rundir=rundir, diff=True, **ansible_kwargs ) - if all( + if "stats" not in results: + ret["comment"] = results.get("stderr", results) + ret["result"] = False + ret["changes"] = {} + elif all( not check["changed"] and not check["failures"] and not check["unreachable"] -- 2.31.1 ++++++ fix-missing-minion-returns-in-batch-mode-360.patch ++++++ >From 83fbfcbf49c98624029f1d215b7ad4d247128d39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hole=C4=8Dek?= <ohole...@aaannz.eu> Date: Mon, 10 May 2021 16:23:19 +0200 Subject: [PATCH] Fix missing minion returns in batch mode (#360) Don't close pub if there are pending events, otherwise events will be lost resulting in empty minion returns. Co-authored-by: Denis V. Meltsaykin <dmeltsay...@mirantis.com> --- salt/client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/client/__init__.py b/salt/client/__init__.py index ddb437604b..78f4d99e84 100644 --- a/salt/client/__init__.py +++ b/salt/client/__init__.py @@ -920,7 +920,7 @@ class LocalClient: self._clean_up_subscriptions(pub_data["jid"]) finally: - if not was_listening: + if not was_listening and not self.event.pending_events: self.event.close_pub() def cmd_full_return( -- 2.31.1 ++++++ fix-save-for-iptables-state-module-bsc-1185131-372.patch ++++++ >From 944f2a8e4db522ad32f547cf350a1268caa6de5a Mon Sep 17 00:00:00 2001 From: Victor Zhestkov <35733135+vzhest...@users.noreply.github.com> Date: Thu, 24 Jun 2021 13:18:51 +0300 Subject: [PATCH] Fix save for iptables state module (bsc#1185131) (#372) --- salt/states/iptables.py | 86 ++++++++------ tests/unit/states/test_iptables.py | 184 ++++++++++++++++++++++++++++- 2 files changed, 227 insertions(+), 43 deletions(-) diff --git a/salt/states/iptables.py b/salt/states/iptables.py index 61dfc7e665..2e81477f18 100644 --- a/salt/states/iptables.py +++ b/salt/states/iptables.py @@ -401,7 +401,7 @@ def append(name, table="filter", family="ipv4", **kwargs): if save: if save_file is True: save_file = None - __salt__["iptables.save"](save_file, family=family) + __salt__["iptables.save"](filename=save_file, family=family) if not ret["changes"]["locale"]: del ret["changes"]["locale"] ret["comment"] = "\n".join(comments) @@ -426,7 +426,9 @@ def append(name, table="filter", family="ipv4", **kwargs): filename = kwargs["save"] else: filename = None - saved_rules = __salt__["iptables.get_saved_rules"](family=family) + saved_rules = __salt__["iptables.get_saved_rules"]( + conf_file=filename, family=family + ) _rules = __salt__["iptables.get_rules"](family=family) __rules = [] for table in _rules: @@ -438,7 +440,7 @@ def append(name, table="filter", family="ipv4", **kwargs): __saved_rules.append(saved_rules[table][chain].get("rules")) # Only save if rules in memory are different than saved rules if __rules != __saved_rules: - out = __salt__["iptables.save"](filename, family=family) + out = __salt__["iptables.save"](filename=filename, family=family) ret["comment"] += ("\nSaved iptables rule {} for {}\n" "{}\n{}").format( name, family, command.strip(), out ) @@ -454,16 +456,15 @@ def append(name, table="filter", family="ipv4", **kwargs): ret["comment"] = "Set iptables rule for {} to: {} for {}".format( name, command.strip(), family ) - if "save" in kwargs: - if kwargs["save"]: - if kwargs["save"] is not True: - filename = kwargs["save"] - else: - filename = None - out = __salt__["iptables.save"](filename, family=family) - ret["comment"] = ( - "Set and saved iptables rule {} for {}\n" "{}\n{}" - ).format(name, family, command.strip(), out) + if "save" in kwargs and kwargs["save"]: + if kwargs["save"] is not True: + filename = kwargs["save"] + else: + filename = None + out = __salt__["iptables.save"](filename=filename, family=family) + ret["comment"] = ( + "Set and saved iptables rule {} for {}\n" "{}\n{}" + ).format(name, family, command.strip(), out) return ret else: ret["result"] = False @@ -527,7 +528,7 @@ def insert(name, table="filter", family="ipv4", **kwargs): if save: if save_file is True: save_file = None - __salt__["iptables.save"](save_file, family=family) + __salt__["iptables.save"](filename=save_file, family=family) if not ret["changes"]["locale"]: del ret["changes"]["locale"] ret["comment"] = "\n".join(comments) @@ -552,7 +553,9 @@ def insert(name, table="filter", family="ipv4", **kwargs): filename = kwargs["save"] else: filename = None - saved_rules = __salt__["iptables.get_saved_rules"](family=family) + saved_rules = __salt__["iptables.get_saved_rules"]( + conf_file=filename, family=family + ) _rules = __salt__["iptables.get_rules"](family=family) __rules = [] for table in _rules: @@ -564,7 +567,7 @@ def insert(name, table="filter", family="ipv4", **kwargs): __saved_rules.append(saved_rules[table][chain].get("rules")) # Only save if rules in memory are different than saved rules if __rules != __saved_rules: - out = __salt__["iptables.save"](filename, family=family) + out = __salt__["iptables.save"](filename=filename, family=family) ret["comment"] += ("\nSaved iptables rule {} for {}\n" "{}\n{}").format( name, family, command.strip(), out ) @@ -582,12 +585,15 @@ def insert(name, table="filter", family="ipv4", **kwargs): ret["comment"] = "Set iptables rule for {} to: {} for {}".format( name, command.strip(), family ) - if "save" in kwargs: - if kwargs["save"]: - out = __salt__["iptables.save"](filename=None, family=family) - ret["comment"] = ( - "Set and saved iptables rule {} for {}\n" "{}\n{}" - ).format(name, family, command.strip(), out) + if "save" in kwargs and kwargs["save"]: + if kwargs["save"] is not True: + filename = kwargs["save"] + else: + filename = None + out = __salt__["iptables.save"](filename=filename, family=family) + ret["comment"] = ( + "Set and saved iptables rule {} for {}\n" "{}\n{}" + ).format(name, family, command.strip(), out) return ret else: ret["result"] = False @@ -646,7 +652,7 @@ def delete(name, table="filter", family="ipv4", **kwargs): if save: if save_file is True: save_file = None - __salt__["iptables.save"](save_file, family=family) + __salt__["iptables.save"](filename=save_file, family=family) if not ret["changes"]["locale"]: del ret["changes"]["locale"] ret["comment"] = "\n".join(comments) @@ -688,12 +694,15 @@ def delete(name, table="filter", family="ipv4", **kwargs): ret["changes"] = {"locale": name} ret["result"] = True ret["comment"] = "Delete iptables rule for {} {}".format(name, command.strip()) - if "save" in kwargs: - if kwargs["save"]: - out = __salt__["iptables.save"](filename=None, family=family) - ret["comment"] = ( - "Deleted and saved iptables rule {} for {}\n" "{}\n{}" - ).format(name, family, command.strip(), out) + if "save" in kwargs and kwargs["save"]: + if kwargs["save"] is not True: + filename = kwargs["save"] + else: + filename = None + out = __salt__["iptables.save"](filename=filename, family=family) + ret["comment"] = ( + "Deleted and saved iptables rule {} for {}\n" "{}\n{}" + ).format(name, family, command.strip(), out) return ret else: ret["result"] = False @@ -751,14 +760,17 @@ def set_policy(name, table="filter", family="ipv4", **kwargs): ret["comment"] = "Set default policy for {} to {} family {}".format( kwargs["chain"], kwargs["policy"], family ) - if "save" in kwargs: - if kwargs["save"]: - __salt__["iptables.save"](filename=None, family=family) - ret[ - "comment" - ] = "Set and saved default policy for {} to {} family {}".format( - kwargs["chain"], kwargs["policy"], family - ) + if "save" in kwargs and kwargs["save"]: + if kwargs["save"] is not True: + filename = kwargs["save"] + else: + filename = None + __salt__["iptables.save"](filename=filename, family=family) + ret[ + "comment" + ] = "Set and saved default policy for {} to {} family {}".format( + kwargs["chain"], kwargs["policy"], family + ) return ret else: ret["result"] = False diff --git a/tests/unit/states/test_iptables.py b/tests/unit/states/test_iptables.py index c49022c962..975ae49c3e 100644 --- a/tests/unit/states/test_iptables.py +++ b/tests/unit/states/test_iptables.py @@ -135,7 +135,7 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin): with patch.object(iptables, "_STATE_INTERNAL_KEYWORDS", mock): mock = MagicMock(return_value="a") with patch.dict(iptables.__salt__, {"iptables.build_rule": mock}): - mock = MagicMock(side_effect=[True, False, False, False]) + mock = MagicMock(side_effect=[True, False, False, False, False, True]) with patch.dict(iptables.__salt__, {"iptables.check": mock}): ret.update( { @@ -161,7 +161,7 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin): ) with patch.dict(iptables.__opts__, {"test": False}): - mock = MagicMock(side_effect=[True, False]) + mock = MagicMock(side_effect=[True, False, True, True]) with patch.dict(iptables.__salt__, {"iptables.append": mock}): ret.update( { @@ -188,6 +188,65 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin): iptables.append("salt", table="", chain=""), ret ) + mock_save = MagicMock( + side_effect=['Wrote 1 lines to "/tmp/iptables"', ""] + ) + with patch.dict( + iptables.__salt__, {"iptables.save": mock_save} + ): + mock_get_saved_rules = MagicMock(side_effect=[""]) + with patch.dict( + iptables.__salt__, + {"iptables.get_saved_rules": mock_get_saved_rules}, + ): + mock = MagicMock(side_effect=[""]) + with patch.dict( + iptables.__salt__, {"iptables.get_rules": mock} + ): + ret.update( + { + "changes": {"locale": "salt"}, + "result": True, + "comment": "Set and saved iptables rule" + ' salt for ipv4\na\nWrote 1 lines to "/tmp/iptables"', + } + ) + self.assertDictEqual( + iptables.append( + "salt", + table="", + chain="", + save="/tmp/iptables", + ), + ret, + ) + ret.update( + { + "changes": {}, + "result": True, + "comment": "iptables rule for salt already set (a) for ipv4", + } + ) + self.assertDictEqual( + iptables.append( + "salt", + table="", + chain="", + save="/tmp/iptables", + ), + ret, + ) + self.assertEqual( + mock_get_saved_rules.mock_calls[0][2][ + "conf_file" + ], + "/tmp/iptables", + ) + self.assertEqual( + mock_save.mock_calls[0][2]["filename"], + "/tmp/iptables", + ) + def test_insert(self): """ Test to insert a rule into a chain @@ -200,7 +259,7 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin): with patch.object(iptables, "_STATE_INTERNAL_KEYWORDS", mock): mock = MagicMock(return_value="a") with patch.dict(iptables.__salt__, {"iptables.build_rule": mock}): - mock = MagicMock(side_effect=[True, False, False, False]) + mock = MagicMock(side_effect=[True, False, False, False, False, True]) with patch.dict(iptables.__salt__, {"iptables.check": mock}): ret.update( { @@ -226,7 +285,7 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin): ) with patch.dict(iptables.__opts__, {"test": False}): - mock = MagicMock(side_effect=[False, True]) + mock = MagicMock(side_effect=[False, True, False, True]) with patch.dict(iptables.__salt__, {"iptables.insert": mock}): ret.update( { @@ -258,6 +317,67 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin): ret, ) + mock_save = MagicMock( + side_effect=['Wrote 1 lines to "/tmp/iptables"', ""] + ) + with patch.dict( + iptables.__salt__, {"iptables.save": mock_save} + ): + mock_get_saved_rules = MagicMock(side_effect=[""]) + with patch.dict( + iptables.__salt__, + {"iptables.get_saved_rules": mock_get_saved_rules}, + ): + mock = MagicMock(side_effect=[""]) + with patch.dict( + iptables.__salt__, {"iptables.get_rules": mock} + ): + ret.update( + { + "changes": {"locale": "salt"}, + "result": True, + "comment": "Set and saved iptables rule" + ' salt for ipv4\na\nWrote 1 lines to "/tmp/iptables"', + } + ) + self.assertDictEqual( + iptables.insert( + "salt", + table="", + chain="", + position="", + save="/tmp/iptables", + ), + ret, + ) + ret.update( + { + "changes": {}, + "result": True, + "comment": "iptables rule for salt already set for ipv4 (a)", + } + ) + self.assertDictEqual( + iptables.insert( + "salt", + table="", + chain="", + position="", + save="/tmp/iptables", + ), + ret, + ) + self.assertEqual( + mock_get_saved_rules.mock_calls[0][2][ + "conf_file" + ], + "/tmp/iptables", + ) + self.assertEqual( + mock_save.mock_calls[0][2]["filename"], + "/tmp/iptables", + ) + def test_delete(self): """ Test to delete a rule to a chain @@ -270,7 +390,7 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin): with patch.object(iptables, "_STATE_INTERNAL_KEYWORDS", mock): mock = MagicMock(return_value="a") with patch.dict(iptables.__salt__, {"iptables.build_rule": mock}): - mock = MagicMock(side_effect=[False, True, True, True]) + mock = MagicMock(side_effect=[False, True, True, True, True, False]) with patch.dict(iptables.__salt__, {"iptables.check": mock}): ret.update( { @@ -296,7 +416,7 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin): ) with patch.dict(iptables.__opts__, {"test": False}): - mock = MagicMock(side_effect=[False, True]) + mock = MagicMock(side_effect=[False, True, False, False]) with patch.dict(iptables.__salt__, {"iptables.delete": mock}): ret.update( { @@ -327,6 +447,58 @@ class IptablesTestCase(TestCase, LoaderModuleMockMixin): ret, ) + mock_save = MagicMock( + side_effect=['Wrote 1 lines to "/tmp/iptables"', ""] + ) + with patch.dict( + iptables.__salt__, {"iptables.save": mock_save} + ): + mock = MagicMock(side_effect=[True, False]) + with patch.dict( + iptables.__salt__, {"iptables.check": mock} + ): + mock = MagicMock(side_effect=[""]) + with patch.dict( + iptables.__salt__, {"iptables.get_rules": mock} + ): + ret.update( + { + "changes": {"locale": "salt"}, + "result": True, + "comment": "Deleted and saved iptables rule" + ' salt for ipv4\na\nWrote 1 lines to "/tmp/iptables"', + } + ) + self.assertDictEqual( + iptables.delete( + "salt", + table="", + chain="", + save="/tmp/iptables", + ), + ret, + ) + ret.update( + { + "changes": {}, + "result": True, + "comment": "iptables rule for salt already absent for ipv4 (a)", + } + ) + self.assertDictEqual( + iptables.delete( + "salt", + table="", + chain="", + save="/tmp/iptables", + ), + ret, + ) + self.assertEqual( + mock_save.mock_calls[0][2]["filename"], + "/tmp/iptables", + ) + def test_set_policy(self): """ Test to sets the default policy for iptables firewall tables -- 2.32.0 ++++++ grains.extra-support-old-non-intel-kernels-bsc-11806.patch ++++++ >From 27c7a9f62b1a589365785c9428293653ac76fee3 Mon Sep 17 00:00:00 2001 From: Alberto Planas <apla...@suse.com> Date: Mon, 10 May 2021 16:26:02 +0200 Subject: [PATCH] grains.extra: support old non-intel kernels (bsc#1180650) (#368) --- salt/grains/extra.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/salt/grains/extra.py b/salt/grains/extra.py index 7729a5c0a5..f2abd1281c 100644 --- a/salt/grains/extra.py +++ b/salt/grains/extra.py @@ -71,10 +71,10 @@ def suse_backported_capabilities(): } -def __secure_boot(): +def __secure_boot(efivars_dir): """Detect if secure-boot is enabled.""" enabled = False - sboot = glob.glob("/sys/firmware/efi/vars/SecureBoot-*/data") + sboot = glob.glob(os.path.join(efivars_dir, "SecureBoot-*/data")) if len(sboot) == 1: # The minion is usually running as a privileged user, but is # not the case for the master. Seems that the master can also @@ -89,9 +89,17 @@ def __secure_boot(): def uefi(): """Populate UEFI grains.""" + efivars_dir = next( + iter( + filter( + os.path.exists, ["/sys/firmware/efi/efivars", "/sys/firmware/efi/vars"] + ) + ), + None, + ) grains = { - "efi": os.path.exists("/sys/firmware/efi/systab"), - "efi-secure-boot": __secure_boot(), + "efi": bool(efivars_dir), + "efi-secure-boot": __secure_boot(efivars_dir) if efivars_dir else False, } return grains -- 2.31.1 ++++++ handle-master-tops-data-when-states-are-applied-by-t.patch ++++++ >From e0b7511e30da289b4100aa156b67b652681afc03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= <psuarezhernan...@suse.com> Date: Thu, 8 Jul 2021 08:57:13 +0100 Subject: [PATCH] Handle "master tops" data when states are applied by "transactional_update" (bsc#1187787) (#398) * Handle master tops data when states are applied by transactional_update (bsc#1187787) * Fix unit tests for transactional_update module --- salt/modules/transactional_update.py | 9 +++++++-- .../unit/modules/test_transactional_update.py | 20 +++++++++---------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/salt/modules/transactional_update.py b/salt/modules/transactional_update.py index 7bbdb697b8..9cdaddb91a 100644 --- a/salt/modules/transactional_update.py +++ b/salt/modules/transactional_update.py @@ -301,6 +301,11 @@ def __virtual__(): return (False, "Module transactional_update requires a transactional system") +class TransactionalUpdateHighstate(salt.client.ssh.state.SSHHighState): + def _master_tops(self): + return self.client.master_tops() + + def _global_params(self_update, snapshot=None, quiet=False): """Utility function to prepare common global parameters.""" params = ["--non-interactive", "--drop-if-no-change"] @@ -1107,7 +1112,7 @@ def sls( # Clone the options data and apply some default values. May not be # needed, as this module just delegate opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) - st_ = salt.client.ssh.state.SSHHighState( + st_ = TransactionalUpdateHighstate( opts, pillar, __salt__, salt.fileclient.get_file_client(__opts__) ) @@ -1180,7 +1185,7 @@ def highstate(activate_transaction=False, **kwargs): # Clone the options data and apply some default values. May not be # needed, as this module just delegate opts = salt.utils.state.get_sls_opts(__opts__, **kwargs) - st_ = salt.client.ssh.state.SSHHighState( + st_ = TransactionalUpdateHighstate( opts, pillar, __salt__, salt.fileclient.get_file_client(__opts__) ) diff --git a/tests/unit/modules/test_transactional_update.py b/tests/unit/modules/test_transactional_update.py index 19e477d02f..2d30f296d7 100644 --- a/tests/unit/modules/test_transactional_update.py +++ b/tests/unit/modules/test_transactional_update.py @@ -622,22 +622,22 @@ class TransactionalUpdateTestCase(TestCase, LoaderModuleMockMixin): utils_mock["files.rm_rf"].assert_called_once() @patch("salt.modules.transactional_update._create_and_execute_salt_state") - @patch("salt.client.ssh.state.SSHHighState") + @patch("salt.modules.transactional_update.TransactionalUpdateHighstate") @patch("salt.fileclient.get_file_client") @patch("salt.utils.state.get_sls_opts") def test_sls( self, get_sls_opts, get_file_client, - SSHHighState, + TransactionalUpdateHighstate, _create_and_execute_salt_state, ): """Test transactional_update.sls""" - SSHHighState.return_value = SSHHighState - SSHHighState.render_highstate.return_value = (None, []) - SSHHighState.state.reconcile_extend.return_value = (None, []) - SSHHighState.state.requisite_in.return_value = (None, []) - SSHHighState.state.verify_high.return_value = [] + TransactionalUpdateHighstate.return_value = TransactionalUpdateHighstate + TransactionalUpdateHighstate.render_highstate.return_value = (None, []) + TransactionalUpdateHighstate.state.reconcile_extend.return_value = (None, []) + TransactionalUpdateHighstate.state.requisite_in.return_value = (None, []) + TransactionalUpdateHighstate.state.verify_high.return_value = [] _create_and_execute_salt_state.return_value = "result" opts_mock = { @@ -649,18 +649,18 @@ class TransactionalUpdateTestCase(TestCase, LoaderModuleMockMixin): _create_and_execute_salt_state.assert_called_once() @patch("salt.modules.transactional_update._create_and_execute_salt_state") - @patch("salt.client.ssh.state.SSHHighState") + @patch("salt.modules.transactional_update.TransactionalUpdateHighstate") @patch("salt.fileclient.get_file_client") @patch("salt.utils.state.get_sls_opts") def test_highstate( self, get_sls_opts, get_file_client, - SSHHighState, + TransactionalUpdateHighstate, _create_and_execute_salt_state, ): """Test transactional_update.highstage""" - SSHHighState.return_value = SSHHighState + TransactionalUpdateHighstate.return_value = TransactionalUpdateHighstate _create_and_execute_salt_state.return_value = "result" opts_mock = { -- 2.32.0 ++++++ handle-volumes-on-stopped-pools-in-virt.vm_info-373.patch ++++++ >From b154f0a17c85c2fe0b85226dfeb3919bd833a85c Mon Sep 17 00:00:00 2001 From: Cedric Bosdonnat <cedric.bosdon...@free.fr> Date: Fri, 21 May 2021 13:04:46 +0200 Subject: [PATCH] Handle volumes on stopped pools in virt.vm_info (#373) For VMs having at least a disk on a stopped volume, we don't want the user to get an exception when running virt.vm_info. Instead just provide less information. --- changelog/60132.fixed | 1 + salt/modules/virt.py | 73 +++++++++++-------- .../pytests/unit/modules/virt/test_domain.py | 9 ++- 3 files changed, 50 insertions(+), 33 deletions(-) create mode 100644 changelog/60132.fixed diff --git a/changelog/60132.fixed b/changelog/60132.fixed new file mode 100644 index 0000000000..1e3bc96b98 --- /dev/null +++ b/changelog/60132.fixed @@ -0,0 +1 @@ +Gracefuly handle errors in virt.vm_info diff --git a/salt/modules/virt.py b/salt/modules/virt.py index 6409089109..d8a8c51ce5 100644 --- a/salt/modules/virt.py +++ b/salt/modules/virt.py @@ -515,41 +515,50 @@ def _get_disks(conn, dom): def _get_disk_volume_data(pool_name, volume_name): qemu_target = "{}/{}".format(pool_name, volume_name) pool = conn.storagePoolLookupByName(pool_name) - vol = pool.storageVolLookupByName(volume_name) - vol_info = vol.info() - extra_properties = { - "virtual size": vol_info[1], - "disk size": vol_info[2], - } - - backing_files = [ - { - "file": node.find("source").get("file"), - "file format": node.find("format").get("type"), + extra_properties = {} + try: + vol = pool.storageVolLookupByName(volume_name) + vol_info = vol.info() + extra_properties = { + "virtual size": vol_info[1], + "disk size": vol_info[2], } - for node in elem.findall(".//backingStore[source]") - ] - if backing_files: - # We had the backing files in a flat list, nest them again. - extra_properties["backing file"] = backing_files[0] - parent = extra_properties["backing file"] - for sub_backing_file in backing_files[1:]: - parent["backing file"] = sub_backing_file - parent = sub_backing_file + backing_files = [ + { + "file": node.find("source").get("file"), + "file format": node.find("format").get("type"), + } + for node in elem.findall(".//backingStore[source]") + ] - else: - # In some cases the backing chain is not displayed by the domain definition - # Try to see if we have some of it in the volume definition. - vol_desc = ElementTree.fromstring(vol.XMLDesc()) - backing_path = vol_desc.find("./backingStore/path") - backing_format = vol_desc.find("./backingStore/format") - if backing_path is not None: - extra_properties["backing file"] = {"file": backing_path.text} - if backing_format is not None: - extra_properties["backing file"][ - "file format" - ] = backing_format.get("type") + if backing_files: + # We had the backing files in a flat list, nest them again. + extra_properties["backing file"] = backing_files[0] + parent = extra_properties["backing file"] + for sub_backing_file in backing_files[1:]: + parent["backing file"] = sub_backing_file + parent = sub_backing_file + + else: + # In some cases the backing chain is not displayed by the domain definition + # Try to see if we have some of it in the volume definition. + vol_desc = ElementTree.fromstring(vol.XMLDesc()) + backing_path = vol_desc.find("./backingStore/path") + backing_format = vol_desc.find("./backingStore/format") + if backing_path is not None: + extra_properties["backing file"] = { + "file": backing_path.text + } + if backing_format is not None: + extra_properties["backing file"][ + "file format" + ] = backing_format.get("type") + except libvirt.libvirtError: + # The volume won't be found if the pool is not started, just output less infos + log.info( + "Couldn't extract all volume informations: pool is likely not running or refreshed" + ) return (qemu_target, extra_properties) if disk_type == "file": diff --git a/tests/pytests/unit/modules/virt/test_domain.py b/tests/pytests/unit/modules/virt/test_domain.py index 76433eaef4..a9453e4a66 100644 --- a/tests/pytests/unit/modules/virt/test_domain.py +++ b/tests/pytests/unit/modules/virt/test_domain.py @@ -192,6 +192,11 @@ def test_get_disks(make_mock_vm, make_mock_storage_pool): <alias name='virtio-disk0'/> <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> </disk> + <disk type='volume' device='disk'> + <driver name='qemu' type='qcow2' cache='none' io='native'/> + <source pool='stopped' volume='vm05_data'/> + <target dev='vdd' bus='virtio'/> + </disk> <disk type='network' device='cdrom'> <driver name='qemu' type='raw' cache='none' io='native'/> <source protocol='http' name='/pub/iso/myimage.iso' query='foo=bar&baz=flurb' index='1'> @@ -205,11 +210,12 @@ def test_get_disks(make_mock_vm, make_mock_storage_pool): </devices> </domain> """ - domain_mock = make_mock_vm(vm_def) + make_mock_vm(vm_def) pool_mock = make_mock_storage_pool( "default", "dir", ["srv01_system", "srv01_data", "vm05_system"] ) + make_mock_storage_pool("stopped", "dir", []) # Append backing store to srv01_data volume XML description srv1data_mock = pool_mock.storageVolLookupByName("srv01_data") @@ -256,6 +262,7 @@ def test_get_disks(make_mock_vm, make_mock_storage_pool): }, }, }, + "vdd": {"type": "disk", "file": "stopped/vm05_data", "file format": "qcow2"}, "hda": { "type": "cdrom", "file format": "raw", -- 2.31.1 ++++++ implementation-of-held-unheld-functions-for-state-pk.patch ++++++ ++++ 903 lines (skipped) ++++++ move-vendor-change-logic-to-zypper-class-355.patch ++++++ ++++ 843 lines (skipped) ++++++ parsing-epoch-out-of-version-provided-during-pkg-rem.patch ++++++ >From d1a8a0d724ee272953bb4615869d9fe468d28e98 Mon Sep 17 00:00:00 2001 From: Jochen Breuer <jbre...@suse.de> Date: Mon, 3 May 2021 17:20:54 +0200 Subject: [PATCH] Parsing Epoch out of version provided during pkg remove (bsc#1173692) yum doesn't seem to like the epoch information provided within the version. Therefore it's removed before passing it to yum. * Introducing `ignore_epoch` to pkg.remove Just like pkg.install pkg.remove now also has ignore_epoch. With this it is possible to ignore the epoch information completely during version comparison. * No epoch regardless of arch * Added tests for cases with and without arch. * Epoch information is now skipped in all cases. * Removes ignore_epoch from pkg state --- changelog/57881.changed | 1 + salt/modules/yumpkg.py | 14 +++-- tests/unit/modules/test_yumpkg.py | 85 +++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 changelog/57881.changed diff --git a/changelog/57881.changed b/changelog/57881.changed new file mode 100644 index 0000000000..e2ae2f4653 --- /dev/null +++ b/changelog/57881.changed @@ -0,0 +1 @@ +Parsing Epoch out of version during pkg remove, since yum can't handle that in all of the cases. diff --git a/salt/modules/yumpkg.py b/salt/modules/yumpkg.py index 82adbbd59d..0fb41a0400 100644 --- a/salt/modules/yumpkg.py +++ b/salt/modules/yumpkg.py @@ -2051,11 +2051,13 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 old = list_pkgs() targets = [] for target in pkg_params: + version_to_remove = pkg_params[target] + installed_versions = old[target].split(",") + # Check if package version set to be removed is actually installed: - # old[target] contains a comma-separated list of installed versions - if target in old and not pkg_params[target]: + if target in old and not version_to_remove: targets.append(target) - elif target in old and pkg_params[target] in old[target].split(","): + elif target in old and version_to_remove in installed_versions: arch = "" pkgname = target try: @@ -2066,7 +2068,11 @@ def remove(name=None, pkgs=None, **kwargs): # pylint: disable=W0613 if archpart in salt.utils.pkg.rpm.ARCHES: arch = "." + archpart pkgname = namepart - targets.append("{}-{}{}".format(pkgname, pkg_params[target], arch)) + # Since we don't always have the arch info, epoch information has to parsed out. But + # a version check was already performed, so we are removing the right version. + targets.append( + "{}-{}{}".format(pkgname, version_to_remove.split(":", 1)[-1], arch) + ) if not targets: return {} diff --git a/tests/unit/modules/test_yumpkg.py b/tests/unit/modules/test_yumpkg.py index 96d3f12b17..e22c0b9251 100644 --- a/tests/unit/modules/test_yumpkg.py +++ b/tests/unit/modules/test_yumpkg.py @@ -1014,6 +1014,91 @@ class YumTestCase(TestCase, LoaderModuleMockMixin): redirect_stderr=True, ) + def test_remove_with_epoch(self): + """ + Tests that we properly identify a version containing an epoch for + deinstallation. + + You can deinstall pkgs only without the epoch if no arch is provided: + + .. code-block:: bash + + yum remove PackageKit-yum-1.1.10-2.el7.centos + """ + name = "foo" + installed = "8:3.8.12-4.n.el7" + list_pkgs_mock = MagicMock( + side_effect=lambda **kwargs: { + name: [installed] + if kwargs.get("versions_as_list", False) + else installed + } + ) + cmd_mock = MagicMock( + return_value={"pid": 12345, "retcode": 0, "stdout": "", "stderr": ""} + ) + salt_mock = { + "cmd.run_all": cmd_mock, + "lowpkg.version_cmp": rpm.version_cmp, + "pkg_resource.parse_targets": MagicMock( + return_value=({name: installed}, "repository") + ), + } + full_pkg_string = "-".join((name, installed[2:])) + with patch.object(yumpkg, "list_pkgs", list_pkgs_mock), patch( + "salt.utils.systemd.has_scope", MagicMock(return_value=False) + ), patch.dict(yumpkg.__salt__, salt_mock): + + with patch.dict(yumpkg.__grains__, {"os": "CentOS", "osrelease": 7}): + expected = ["yum", "-y", "remove", full_pkg_string] + yumpkg.remove(name) + call = cmd_mock.mock_calls[0][1][0] + assert call == expected, call + + def test_remove_with_epoch_and_arch_info(self): + """ + Tests that we properly identify a version containing an epoch and arch + deinstallation. + + You can deinstall pkgs with or without epoch in combination with the arch. + Here we test for the absence of the epoch, but the presence for the arch: + + .. code-block:: bash + + yum remove PackageKit-yum-1.1.10-2.el7.centos.x86_64 + """ + arch = "x86_64" + name = "foo" + name_and_arch = name + "." + arch + installed = "8:3.8.12-4.n.el7" + list_pkgs_mock = MagicMock( + side_effect=lambda **kwargs: { + name_and_arch: [installed] + if kwargs.get("versions_as_list", False) + else installed + } + ) + cmd_mock = MagicMock( + return_value={"pid": 12345, "retcode": 0, "stdout": "", "stderr": ""} + ) + salt_mock = { + "cmd.run_all": cmd_mock, + "lowpkg.version_cmp": rpm.version_cmp, + "pkg_resource.parse_targets": MagicMock( + return_value=({name_and_arch: installed}, "repository") + ), + } + full_pkg_string = "-".join((name, installed[2:])) + with patch.object(yumpkg, "list_pkgs", list_pkgs_mock), patch( + "salt.utils.systemd.has_scope", MagicMock(return_value=False) + ), patch.dict(yumpkg.__salt__, salt_mock): + + with patch.dict(yumpkg.__grains__, {"os": "CentOS", "osrelease": 7}): + expected = ["yum", "-y", "remove", full_pkg_string + "." + arch] + yumpkg.remove(name) + call = cmd_mock.mock_calls[0][1][0] + assert call == expected, call + def test_install_with_epoch(self): """ Tests that we properly identify a version containing an epoch as an -- 2.31.1 ++++++ prevent-command-injection-in-the-snapper-module-bsc-.patch ++++++ >From ea02e9398160fad03dd662635ec038b95db2c04a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20Su=C3=A1rez=20Hern=C3=A1ndez?= <psuarezhernan...@suse.com> Date: Tue, 27 Apr 2021 11:14:20 +0100 Subject: [PATCH] Prevent command injection in the snapper module (bsc#1185281) (CVE-2021-31607) --- salt/modules/snapper.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/salt/modules/snapper.py b/salt/modules/snapper.py index 1df3ce9368..6954c3b544 100644 --- a/salt/modules/snapper.py +++ b/salt/modules/snapper.py @@ -18,6 +18,7 @@ from __future__ import absolute_import, print_function, unicode_literals import difflib import logging import os +import subprocess import time import salt.utils.files @@ -561,8 +562,13 @@ def _is_text_file(filename): """ Checks if a file is a text file """ - type_of_file = os.popen("file -bi {0}".format(filename), "r").read() - return type_of_file.startswith("text") + type_of_file = subprocess.run( + ["file", "-bi", filename], + check=False, + stdout=subprocess.PIPE, + universal_newlines=True, + ).stdout + return type_of_file.startswith('text') def run(function, *args, **kwargs): -- 2.31.1 ++++++ salt-tmpfiles.d ++++++ --- /var/tmp/diff_new_pack.WWBdJC/_old 2021-09-20 23:33:03.583166649 +0200 +++ /var/tmp/diff_new_pack.WWBdJC/_new 2021-09-20 23:33:03.583166649 +0200 @@ -1,5 +1,5 @@ # Type Path Mode UID GID Age Argument -d /var/run/salt 0750 root salt -d /var/run/salt/master 0750 salt salt -d /var/run/salt/minion 0750 root root +d /run/salt 0750 root salt +d /run/salt/master 0750 salt salt +d /run/salt/minion 0750 root root ++++++ templates-move-the-globals-up-to-the-environment-jin.patch ++++++ >From 1e8f506827bcf32bfe7e87763fa854a13729f2c8 Mon Sep 17 00:00:00 2001 From: Alberto Planas <apla...@suse.com> Date: Tue, 31 Aug 2021 11:20:49 +0200 Subject: [PATCH] templates: move the globals up to the Environment (Jinja2 3.0.0) (#418) * jinja: fix TemplateNotFound missing name The TemplateNotFound exception requires a parameter, name, that is missing in one of the calls. File "/usr/lib/python3.8/site-packages/salt/utils/jinja.py", line 158, in get_source raise TemplateNotFound TypeError: __init__() missing 1 required positional argument: 'name' This patch add the missing parameter in the raise call. Signed-off-by: Alberto Planas <apla...@suse.com> * templates: move the globals up to the Environment When creating a Jinja2 environment, we populate the globals in the Template object that we generate from the environment. This cause a problem when there is a {% include "./file.sls" %} in the template, as cannot find in the environment globals information like the "tpldir", for example, making the relative path to be unresolved. Seems that in Jinja2 2.X this behaviour is not present, so attaching the globals to the Template will make the include to work, but since Jinja2 3.0.0 this is not the case. Maybe related with the re-architecture from https://github.com/pallets/jinja/issues/295 This patch populate the globals in the Environment level, making this and other variables reachable by the Jinja templates. Fix #55159 Signed-off-by: Alberto Planas <apla...@suse.com> --- changelog/55159.fixed | 1 + salt/utils/jinja.py | 2 +- salt/utils/templates.py | 2 +- tests/unit/utils/test_jinja.py | 16 ++++++++++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 changelog/55159.fixed diff --git a/changelog/55159.fixed b/changelog/55159.fixed new file mode 100644 index 0000000000..6ee1a78366 --- /dev/null +++ b/changelog/55159.fixed @@ -0,0 +1 @@ +Jinja renderer resolves wrong relative paths when importing subdirectories diff --git a/salt/utils/jinja.py b/salt/utils/jinja.py index 997d4b1697..e1ac4657f9 100644 --- a/salt/utils/jinja.py +++ b/salt/utils/jinja.py @@ -155,7 +155,7 @@ class SaltCacheLoader(BaseLoader): 'Relative path "%s" cannot be resolved without an environment', template, ) - raise TemplateNotFound + raise TemplateNotFound(template) base_path = environment.globals["tpldir"] _template = os.path.normpath("/".join((base_path, _template))) if _template.split("/", 1)[0] == "..": diff --git a/salt/utils/templates.py b/salt/utils/templates.py index 1fda960b2e..f369da5c9e 100644 --- a/salt/utils/templates.py +++ b/salt/utils/templates.py @@ -492,9 +492,9 @@ def render_jinja_tmpl(tmplstr, context, tmplpath=None): ) decoded_context[key] = salt.utils.data.decode(value) + jinja_env.globals.update(decoded_context) try: template = jinja_env.from_string(tmplstr) - template.globals.update(decoded_context) output = template.render(**decoded_context) except jinja2.exceptions.UndefinedError as exc: trace = traceback.extract_tb(sys.exc_info()[2]) diff --git a/tests/unit/utils/test_jinja.py b/tests/unit/utils/test_jinja.py index 807e901afa..0219512097 100644 --- a/tests/unit/utils/test_jinja.py +++ b/tests/unit/utils/test_jinja.py @@ -612,6 +612,22 @@ class TestGetTemplate(TestCase): dict(opts=self.local_opts, saltenv="test", salt=self.local_salt), ) + def test_relative_include(self): + template = "{% include './hello_import' %}" + expected = "Hey world !a b !" + filename = os.path.join(self.template_dir, "hello_import") + with salt.utils.files.fopen(filename) as fp_: + out = render_jinja_tmpl( + template, + dict( + opts=self.local_opts, + saltenv="test", + salt=self.local_salt, + tpldir=self.template_dir, + ), + ) + self.assertEqual(out, expected) + class TestJinjaDefaultOptions(TestCase): @classmethod -- 2.33.0 ++++++ transactional_update-detect-recursion-in-the-executo.patch ++++++ >From 1ea573fe35245ab08eb26a757d373ca16c841a1c Mon Sep 17 00:00:00 2001 From: Alberto Planas <apla...@suse.com> Date: Tue, 27 Apr 2021 14:01:43 +0200 Subject: [PATCH] transactional_update: detect recursion in the executor (#359) --- salt/executors/transactional_update.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/salt/executors/transactional_update.py b/salt/executors/transactional_update.py index ef7d92bc05..0fa83d730b 100644 --- a/salt/executors/transactional_update.py +++ b/salt/executors/transactional_update.py @@ -5,6 +5,8 @@ Transactional executor module """ +import os + import salt.utils.path # Functions that are mapped into an equivalent one in @@ -98,6 +100,8 @@ def execute(opts, data, func, args, kwargs): add_delegated_functions: [file.copy] """ + inside_transaction = os.environ.get("TRANSACTIONAL_UPDATE") + fun = data["fun"] module, _ = fun.split(".") @@ -114,11 +118,13 @@ def execute(opts, data, func, args, kwargs): delegated_modules |= set(opts.get("add_delegated_modules", [])) delegated_functions |= set(opts.get("add_delegated_functions", [])) - if fun in DELEGATION_MAP: + if fun in DELEGATION_MAP and not inside_transaction: result = __executors__["direct_call.execute"]( opts, data, __salt__[DELEGATION_MAP[fun]], args, kwargs ) - elif module in delegated_modules or fun in delegated_functions: + elif ( + module in delegated_modules or fun in delegated_functions + ) and not inside_transaction: result = __salt__["transactional_update.call"](fun, *args, **kwargs) else: result = __executors__["direct_call.execute"](opts, data, func, args, kwargs) -- 2.31.1 ++++++ transactional_update.conf ++++++ # Enable the transactional_update executor module_executors: - transactional_update - direct_call ++++++ virt-pass-emulator-when-getting-domain-capabilities-.patch ++++++ >From ddcf5ae80be638ade7634990194c48c5c703d538 Mon Sep 17 00:00:00 2001 From: Cedric Bosdonnat <cedric.bosdon...@free.fr> Date: Tue, 6 Jul 2021 08:47:25 +0200 Subject: [PATCH] virt: pass emulator when getting domain capabilities from libvirt (#394) On aarch64, for some emulated architectures like armv6l libvirt needs to have the emulator path to properly return the domain capabilities. Passing it will avoid virt.all_capabilities to fail on such architectures. --- changelog/60491.fixed | 1 + salt/modules/virt.py | 10 +++++++--- tests/unit/modules/test_virt.py | 4 ++++ 3 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 changelog/60491.fixed diff --git a/changelog/60491.fixed b/changelog/60491.fixed new file mode 100644 index 0000000000..256d29b5fb --- /dev/null +++ b/changelog/60491.fixed @@ -0,0 +1 @@ +Pass emulator path to get guest capabilities from libvirt diff --git a/salt/modules/virt.py b/salt/modules/virt.py index 2f2aa63957..12b39d76db 100644 --- a/salt/modules/virt.py +++ b/salt/modules/virt.py @@ -6770,7 +6770,11 @@ def all_capabilities(**kwargs): host_caps = ElementTree.fromstring(conn.getCapabilities()) domains = [ [ - (guest.get("arch", {}).get("name", None), key) + ( + guest.get("arch", {}).get("name", None), + key, + guest.get("arch", {}).get("emulator", None), + ) for key in guest.get("arch", {}).get("domains", {}).keys() ] for guest in [ @@ -6788,10 +6792,10 @@ def all_capabilities(**kwargs): "domains": [ _parse_domain_caps( ElementTree.fromstring( - conn.getDomainCapabilities(None, arch, None, domain) + conn.getDomainCapabilities(emulator, arch, None, domain) ) ) - for (arch, domain) in flattened + for (arch, domain, emulator) in flattened ], } finally: diff --git a/tests/unit/modules/test_virt.py b/tests/unit/modules/test_virt.py index 5c7e1e1cc4..c6a76af10f 100644 --- a/tests/unit/modules/test_virt.py +++ b/tests/unit/modules/test_virt.py @@ -5057,6 +5057,10 @@ class VirtTestCase(TestCase, LoaderModuleMockMixin): {"qemu", "kvm"}, {domainCaps["domain"] for domainCaps in caps["domains"]}, ) + self.mock_conn.getDomainCapabilities.assert_called_with( + "/usr/bin/qemu-system-x86_64", "x86_64", None, "kvm" + ) + def test_network_tag(self): """ Test virt._get_net_xml() with VLAN tag -- 2.32.0 ++++++ virt-use-dev-kvm-to-detect-kvm-383.patch ++++++ >From 73f474fcc7700abff110e3eac653fea5e320ee4f Mon Sep 17 00:00:00 2001 From: Cedric Bosdonnat <cedric.bosdon...@free.fr> Date: Thu, 24 Jun 2021 11:37:41 +0200 Subject: [PATCH] virt: use /dev/kvm to detect KVM (#383) checking for kvm_* modules to be loaded is not robust enough since the kernel could be compiled with builtin modules. /dev/kvm is much more reliable. --- changelog/60419.fixed | 1 + salt/modules/virt.py | 7 +------ tests/pytests/unit/modules/virt/test_host.py | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 changelog/60419.fixed diff --git a/changelog/60419.fixed b/changelog/60419.fixed new file mode 100644 index 0000000000..44c782da48 --- /dev/null +++ b/changelog/60419.fixed @@ -0,0 +1 @@ +Check for /dev/kvm to detect KVM hypervisor. diff --git a/salt/modules/virt.py b/salt/modules/virt.py index d8a8c51ce5..2f2aa63957 100644 --- a/salt/modules/virt.py +++ b/salt/modules/virt.py @@ -5745,12 +5745,7 @@ def _is_kvm_hyper(): """ Returns a bool whether or not this node is a KVM hypervisor """ - try: - with salt.utils.files.fopen("/proc/modules") as fp_: - if "kvm_" not in salt.utils.stringutils.to_unicode(fp_.read()): - return False - except OSError: - # No /proc/modules? Are we on Windows? Or Solaris? + if not os.path.exists("/dev/kvm"): return False return "libvirtd" in __salt__["cmd.run"](__grains__["ps"]) diff --git a/tests/pytests/unit/modules/virt/test_host.py b/tests/pytests/unit/modules/virt/test_host.py index 6c9ac79337..c5cadb8aa0 100644 --- a/tests/pytests/unit/modules/virt/test_host.py +++ b/tests/pytests/unit/modules/virt/test_host.py @@ -1,5 +1,8 @@ +import os.path + import pytest import salt.modules.virt as virt +from tests.support.mock import MagicMock, patch from .conftest import loader_modules_config @@ -217,3 +220,19 @@ def test_node_devices(make_mock_device): "device name": "pci_0000_02_10_7", }, ] + + +@pytest.mark.parametrize( + "dev_kvm, libvirtd", [(True, True), (False, False), (True, False)] +) +def test_is_kvm(dev_kvm, libvirtd): + """ + Test the virt._is_kvm_hyper() function + """ + with patch.dict(os.path.__dict__, {"exists": MagicMock(return_value=dev_kvm)}): + processes = ["libvirtd"] if libvirtd else [] + with patch.dict(virt.__grains__, {"ps": MagicMock(return_value="foo")}): + with patch.dict( + virt.__salt__, {"cmd.run": MagicMock(return_value=processes)} + ): + assert virt._is_kvm_hyper() == (dev_kvm and libvirtd) -- 2.31.1