Skip to content

[BUG] When prereq is used in file.line, test=True becomes invalid #67987

Open

Description

@gordonwwang

Description
According to the document, prereq only executes test=True and does not actually apply the configuration.
But I found that when prereq is used for file.line, the configuration will really be applied.

This will lead to two problems:

  1. The file had already been modified during the backup. Backup failed
  2. When we execute file.line to modify the problem, satl will prompt "no changes need to xxx".

Setup
pillar contents:

pam:
  security_limits:
    conf_path: /etc/security/limits.conf
    add_lines:
      - content: '@student        -       maxlogins       4'
        after: '#@student        -       maxlogins       4'
    conf_cmd:
      - lss -hl

state file:

{% set backup_dir = '/root/' %}

{% for pam_item, pam_data in pillar.get('pam', {}).items() %}
  {% set full_path = pam_data.get('conf_path', '') %}  
  {% set safe_path = full_path  | replace('/', '_') | replace('.', '_') %}
  {% set backup_file = 'pam_' ~ pam_item ~ '_' ~ safe_path ~ '.backup' %}
  {% set backup_path = backup_dir + backup_file %}

debug_file_content_before_backup:
  cmd.run:
    - name: echo "===BEFORE backup===" && cat {{ full_path }} | grep -C1 "#@student"

# backup file
backup_content_{{ pam_item }}:
  cmd.run:
    - name: |
        cp -aL {{ full_path }} {{ backup_path }}
        echo "备份创建于$(date +'%Y-%m-%d %H:%M:%S.%N')"
        echo "===WHEN backup===" && cat {{ full_path }} | grep -C1 "#@student"
    - prereq:
      {% for line in pam_data.get('add_lines', []) %}
      - file: pam_{{ pam_item }}_add_line_{{ loop.index }}
      {% endfor %}

debug_file_content_before:
  cmd.run:
    - name: echo "===BEFORE CHANGES===" && cat {{ full_path }} | grep -C1 "#@student"

  # add line
  {% for line in pam_data.get('add_lines', []) %}
  {% set line_state = 'pam_' + pam_item + '_add_line_' + loop.index|string %}

{{ line_state }}:
  file.line:
    - name: {{ full_path }}
    - content: "{{ line['content'] }}"
    - mode: ensure
    #- backup: '.bak'
    - match: "{{ line.get('match', line['content']) }}"
    {% if line.get('before') %}
    - before: "{{ line['before'] }}"
    {% endif %}
    {% if line.get('after') %}
    - after: "{{ line['after'] }}"
    {% endif %}

  {% endfor %}

  # remove line...

{% endfor %}

Steps to Reproduce the behavior
execute salt-call --local state.apply , output as fellow:

local:
----------
          ID: debug_file_content_before_backup
    Function: cmd.run
        Name: echo "===BEFORE backup===" && cat /etc/security/limits.conf | grep -C1 "#@student"
      Result: True
     Comment: Command "echo "===BEFORE backup===" && cat /etc/security/limits.conf | grep -C1 "#@student"" run
     Started: 09:16:17.821768
    Duration: 3.737 ms
     Changes:   
              ----------
              pid:
                  116855
              retcode:
                  0
              stderr:
              stdout:
                  ===BEFORE backup===
                  --
                  #ftp             hard    nproc           0
                  #@student        -       maxlogins       4
----------
          ID: backup_content_security_limits
    Function: cmd.run
        Name: cp -aL /etc/security/limits.conf /root/pam_security_limits__etc_security_limits_conf.backup
echo "备份创建于$(date +'%Y-%m-%d %H:%M:%S.%N')"
echo "===WHEN backup===" && cat /etc/security/limits.conf | grep -C1 "#@student"

      Result: True
     Comment: Command "cp -aL /etc/security/limits.conf /root/pam_security_limits__etc_security_limits_conf.backup
              echo "备份创建于$(date +'%Y-%m-%d %H:%M:%S.%N')"
              echo "===WHEN backup===" && cat /etc/security/limits.conf | grep -C1 "#@student"
              " run
     Started: 09:16:17.833017
    Duration: 6.487 ms
     Changes:   
              ----------
              pid:
                  116858
              retcode:
                  0
              stderr:
              stdout:
                  备份创建于2025-04-10 09:16:17.836633182
                  ===WHEN backup===
                  --
                  #ftp             hard    nproc           0
                  #@student        -       maxlogins       4
                  @student        -       maxlogins       4
----------
          ID: debug_file_content_before
    Function: cmd.run
        Name: echo "===BEFORE CHANGES===" && cat /etc/security/limits.conf | grep -C1 "#@student"
      Result: True
     Comment: Command "echo "===BEFORE CHANGES===" && cat /etc/security/limits.conf | grep -C1 "#@student"" run
     Started: 09:16:17.839617
    Duration: 2.635 ms
     Changes:   
              ----------
              pid:
                  116863
              retcode:
                  0
              stderr:
              stdout:
                  ===BEFORE CHANGES===
                  --
                  #ftp             hard    nproc           0
                  #@student        -       maxlogins       4
                  @student        -       maxlogins       4
  Name: /etc/security/limits.conf - Function: file.line - Result: Clean - Started: 09:16:17.842445 - Duration: 0.968 ms

Summary for local
------------
Succeeded: 4 (changed=3)
Failed:    0
------------
Total states run:     4
Total run time:  13.827 ms

Expected behavior
before backup, output:

                  ===BEFORE backup===
                  --
                  #ftp             hard    nproc           0
                  #@student        -       maxlogins       4

when backup, output:

                  ===WHEN backup===
                  --
                  #ftp             hard    nproc           0
                  #@student        -       maxlogins       4

Screenshots
If applicable, add screenshots to help explain your problem.

Image

Versions Report

salt --versions-report (Provided by running salt --versions-report. Please also mention any differences in master/minion versions.)
# salt-call --version
salt-call 3006.8 (Sulfur)

Additional context
Add any other context about the problem here.

Activity

  1. welcome commented on Apr 25, 2025

    @welcome

    Hi there! Welcome to the Salt Community! Thank you for making your first contribution. We have a lengthy process for issues and PRs. Someone from the Core Team will follow up as soon as possible. In the meantime, here’s some information that may help as you continue your Salt journey.
    Please be sure to review our Code of Conduct. Also, check out some of our community resources including:

    There are lots of ways to get involved in our community. Every month, there are around a dozen opportunities to meet with other contributors and the Salt Core team and collaborate in real time. The best way to keep track is by subscribing to the Salt Community Events Calendar.
    If you have additional questions, email us at saltproject.pdl@broadcom.com. We’re glad you’ve joined our community and look forward to doing awesome things with you!

  2. bdrx312 commented on Sep 8, 2025

    @bdrx312
    Contributor

    According to the document, prereq only executes test=True and does not actually apply the configuration.

    According to what document? prereq first executes a state with test=True to determine if it needs to run, but then runs a state if it is determined that the target state need to run. https://docs.saltproject.io/en/latest/ref/states/requisites.html#prereq

    Your block below says to first run each pam_{{ pam_item }}_add_line_{{ loop.index }} file.line state with test=True, if it needs to run then run the cmd.run state, and then finally actually run each of the file.line states without the test=True.

    backup_content_{{ pam_item }}:
      cmd.run:
        - name: |
            cp -aL {{ full_path }} {{ backup_path }}
            echo "备份创建于$(date +'%Y-%m-%d %H:%M:%S.%N')"
            echo "===WHEN backup===" && cat {{ full_path }} | grep -C1 "#@student"
        - prereq:
          {% for line in pam_data.get('add_lines', []) %}
          - file: pam_{{ pam_item }}_add_line_{{ loop.index }}
          {% endfor %}

    Perhaps you are wanting the functionality of one of the other requisites like onchanges https://docs.saltproject.io/en/latest/ref/states/requisites.html#onchanges

  3. gordonwwang commented on Sep 9, 2025

    @gordonwwang
    Author

    @bdrx312
    Hello, thank you for your response. However, I think there have been some misunderstandings between us.
    Let me explain:

    1. further elaborate on the issues with file.line and prereq
    My requirement is as follows:
    Firstly, use prereq to check whether file.line has changed (with test=True, just a dryrun,it will not actually execute file.line). If there is a change, execute cmd.run(cp -aL origin_file backup_file) to copy the file, and then execute file.line; if there is no change, there is no need to execute cp.
    The current problem is: When using prereq to check if file.line will change, it seems to actually execute file.line, modifies full_path, instead of test=True (this is a bug); then when executing file.line, salt outputs "no changes needed for xxx".

    2. Why did I choose "prereq" instead of "onchanges"?
    Because I need to back up the original file before executing file.line (if file.line modifies the original file).
    And in the case of "onchanges", it seems that the file is backed up only after the "file.line" command is executed. At this point, the file being copied is the one that has been modified.

    By the way:
    When using prereq in file.managed, file.serialize, and file.replace, there is no such problem.
    Only when using prereq with file.line does the bug occur.

  4. bdrx312 commented on Sep 9, 2025

    @bdrx312
    Contributor

    I ran a simple test and reproduced the same Comment: No changes needed to be made. I think the bug is only a reporting issue; I ran the simple test below to check if the file.line prereq actual behavior functions as expected.

    • /srv/salt/testing.sls
    "test file.line":
      file.line:
        - name: /tmp/testing.txt
        - content: "add line\n"
        - mode: ensure
        - after: "^first line.*$"
     
    "mv /tmp/testing.txt /tmp/testing.txt.bak":
      cmd.run:
        - prereq:
          - file: "test file.line"
    
    • /tmp/testing.txt
    first line
    
    • salt-call --local state.apply testing
    $ salt-call --local state.apply testing
    local:
    ----------
    ID: mv /tmp/testing.txt /tmp/testing.txt.bak
    ...
    ----------
    ID: test file.line
    Function: file.line
    Name: /tmp/testing.txt
    Result: False
    Comment: /tmp/testing.txt: file not found
    Started: 13:00:30.762512
    Duration: 0.778 ms
    Changes: 

    Are you experiencing the command not getting run before the the file.line changes the file or are you just noticing the report output is not expected?

  5. added
    severity-low4th level, cosemtic problems, work around exists
    on Oct 21, 2026
  6. added 2 commits that reference this issue on Nov 11, 2026
    3ff8913
    75a0fdd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugbroken, incorrect, or confusing behaviorneeds-triageseverity-low4th level, cosemtic problems, work around exists

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions