salt icon indicating copy to clipboard operation
salt copied to clipboard

[BUG] `prereq`'d state with `onlyif`/`unless` + `fun`: onlyif requisite ignored

Open lkubb opened this issue 2 years ago • 5 comments

Description A state that is prerequired and itself has an onlyif (or unless) requisite that calls an execution module will run regardless of outcome of the onlyif evaluation.

Setup

  • [x] on-prem machine
  • [x] VM (Virtualbox, KVM, etc. please specify)
  • [ ] VM running on a cloud service, please be explicit and add details
  • [ ] container (Kubernetes, Docker, containerd, etc. please specify)
  • [ ] or a combination, please be explicit
  • [ ] jails if it is FreeBSD
  • [ ] classic packaging
  • [x] onedir packaging
  • [x] used bootstrap to install

Steps to Reproduce the behavior

Copy stuff:
  file.copy:
    - name: /tmp/aa
    - source: /tmp/a
    - onlyif:
      - fun: file.file_exists
        path: /tmp/a

Do sth else:
  cmd.run:
    - name: true
    - prereq:
      - Copy stuff

Expected behavior

local:
----------
          ID: Do sth else
    Function: cmd.run
      Result: True
     Comment: No changes detected
     Started: 06:41:33.485261
    Duration: 0.007 ms
     Changes:
----------
          ID: Copy stuff
    Function: file.copy
        Name: /tmp/aa
      Result: True
     Comment: onlyif condition is false
     Started: 06:41:33.485360
    Duration: 8.103 ms
     Changes:

Summary for local
------------
Succeeded: 2
Failed:    0
------------
Total states run:     2
Total run time:   8.110 ms

Screenshots

[WARNING ] no `fun` argument in onlyif: OrderedDict([('path', '/tmp/a')])
[ERROR   ] Source file "/tmp/a" is not present
local:
----------
          ID: Do sth else
    Function: cmd.run
      Result: True
     Comment: No changes detected
     Started: 06:37:15.121723
    Duration: 0.008 ms
     Changes:
----------
          ID: Copy stuff
    Function: file.copy
        Name: /tmp/aa
      Result: False
     Comment: Source file "/tmp/a" is not present
     Started: 06:37:15.121817
    Duration: 80.259 ms
     Changes:

Summary for local
------------
Succeeded: 1
Failed:    1
------------
Total states run:     2
Total run time:  80.267 ms

Versions Report

salt --versions-report (Provided by running salt --versions-report. Please also mention any differences in master/minion versions.)
Salt Version:
          Salt: 3006.3

Python Version:
        Python: 3.10.13 (main, Sep  6 2023, 02:11:27) [GCC 11.2.0]

Dependency Versions:
          cffi: 1.14.6
      cherrypy: unknown
      dateutil: 2.8.1
     docker-py: Not Installed
         gitdb: Not Installed
     gitpython: Not Installed
        Jinja2: 3.1.2
       libgit2: Not Installed
  looseversion: 1.0.2
      M2Crypto: Not Installed
          Mako: Not Installed
       msgpack: 1.0.2
  msgpack-pure: Not Installed
  mysql-python: Not Installed
     packaging: 22.0
     pycparser: 2.21
      pycrypto: Not Installed
  pycryptodome: 3.9.8
        pygit2: Not Installed
  python-gnupg: 0.4.8
        PyYAML: 6.0.1
         PyZMQ: 23.2.0
        relenv: 0.13.10
         smmap: Not Installed
       timelib: 0.2.4
       Tornado: 4.5.3
           ZMQ: 4.3.4

System Versions:
          dist: rocky 9.2 Blue Onyx
        locale: utf-8
       machine: x86_64
       release: 5.14.0-284.18.1.el9_2.x86_64
        system: Linux
       version: Rocky Linux 9.2 Blue Onyx

Additional context The prereq results in the state to be run twice. During the first run, fun (and args) is popped from the passed-in lowstate chunk and is thus unavailable for the second (non-test) run:

https://github.com/saltstack/salt/blob/fb717a8d4bea3d48c596ae8dda313b7639c23dc6/salt/state.py#L1028-L1034

lkubb avatar Sep 25 '23 06:09 lkubb

The lowstate will need to be deep-cloned at an early stage. Hopefully there isn't a feature that relies on being able to change it globally in a state run...

OrangeDog avatar Sep 25 '23 11:09 OrangeDog

This also occurs when using unless instead of onlyif. Why is this scheduled for Argon?

Pheric avatar Nov 01 '23 15:11 Pheric

I think this bugs does not occur any more, the following snippet run correctly in a stateful manner and all cases are correctly covered (watch_in, prereq_in, require_in, unless) :

{%- set master_modules_deps = ['pygit2', 'python-ldap', 'psycopg2-binary'] %}
{%- set master_modules_deps_run_deps =  [ "git" ] %}
{%- set master_modules_deps_build_deps = [ "gcc", "libldap2-dev", "libsasl2-dev" ] %} # <- Debian specific

master_modules_deps_run_deps_pkg_installed:
  pkg.latest:
    - names: {{ master_modules_deps_run_deps }}
    - update: True

master_modules_deps_build_deps_pkg_installed:
  pkg.latest:
    - names: {{ master_modules_deps_build_deps }}
    - update: True

master_modules_deps_downgrade_pip_workaround_pip_installed:
  pip.installed:
    - name: pip==22.3.1

{%- for master_modules_dep in master_modules_deps %}
master_modules_dep_{{ master_modules_dep }}_pip_installed:
  pip.installed:
    - name: {{ master_modules_dep }}
    - upgrade: True
    - unless:  # dep installed and up to date
      - fun: pip.is_installed
        pkgname: {{ master_modules_dep }}
      - fun: cmd.run
        cmd: ! salt-pip list --outdated | grep {{ master_modules_dep }}
    - prereq_in:
      - pkg: master_modules_deps_run_deps_pkg_installed
      - pkg: master_modules_deps_build_deps_pkg_installed
      - pip: master_modules_deps_downgrade_pip_workaround_pip_installed
    - require_in:
      - pkg: master_modules_deps_build_dep_pkg_removed
    - watch_in:
      - service: salt-master
{%- endfor %}

master_modules_deps_build_dep_pkg_removed:
  pkg.removed:
    - names: {{ master_modules_deps_build_deps }}

master_pip_pip_update:
  pip.installed:
    - name: pip
    - upgrade: true
    - onlyif:
      - fun: pip.upgrade_available
        pkg: pip

I just get a warning that is irrelevant as the states and logic runs correctly:

[WARNING ] no `fun` argument in unless: OrderedDict([('pkgname', 'pygit2')])
[WARNING ] no `fun` argument in unless: OrderedDict([('pkgname', 'python-ldap')])
[WARNING ] no `fun` argument in unless: OrderedDict([('pkgname', 'psycopg2-binary')])

It is great to finally have this kind of sls run correctly, it wasn't the case few months ago

sticky-note avatar Feb 03 '24 15:02 sticky-note

@sticky-note Are you absolutely certain it's running as expected? The ~~error~~ log message is the same as before and is logged just before the instruction is skipped.

lkubb avatar Feb 03 '24 15:02 lkubb

In my case, with this code snippet, all cases are handled correctly, I am running:

salt-call --versions
Salt Version:
          Salt: 3006.6
 
Python Version:
        Python: 3.10.13 (main, Nov 15 2023, 04:34:27) [GCC 11.2.0]
 
Dependency Versions:
          cffi: 1.16.0
      cherrypy: 18.6.1
      dateutil: 2.8.1
     docker-py: Not Installed
         gitdb: Not Installed
     gitpython: Not Installed
        Jinja2: 3.1.3
       libgit2: 1.7.1
  looseversion: 1.0.2
      M2Crypto: Not Installed
          Mako: Not Installed
       msgpack: 1.0.2
  msgpack-pure: Not Installed
  mysql-python: Not Installed
     packaging: 22.0
     pycparser: 2.21
      pycrypto: Not Installed
  pycryptodome: 3.19.1
        pygit2: 1.14.0
  python-gnupg: 0.4.8
        PyYAML: 6.0.1
         PyZMQ: 23.2.0
        relenv: 0.14.2
         smmap: Not Installed
       timelib: 0.2.4
       Tornado: 4.5.3
           ZMQ: 4.3.4
 
System Versions:
          dist: debian 12 bookworm
        locale: utf-8
       machine: x86_64
       release: 6.1.0-17-amd64
        system: Linux
       version: Debian GNU/Linux 12 bookworm

prereq_in and require_in are correctly run when they should and the (AND) condition on unless also. The only case when this code snippets does nothing is when all master_modules_deps are installed AND up to date.

Hope this is useful

sticky-note avatar Feb 04 '24 11:02 sticky-note