What's new in Salt 3005 Phosphorus

25 minute read Updated:

Salt Phosphorus

It’s been 10 months and 2 CVE releases since the last major Salt version. If you do not have time to dig into all the cool stuff that went in, just read the first section about onedir packages because they will affect you sooner or later.

NOTE: This is a draft version of the article, shared first with my subscribers. I'll publish it within a week or so. Proofreading and feedback are appreciated! 🙏

What's new in Salt 3005 Phosphorus: Onedir (Tiamat) packages, New SEPs, Internal refactorings, RESTCONF Proxy, Clouds, Ansible improvements, Nifty tricks.

Onedir packages

Salt 3005 Phosphorus starts to use a new packaging tool called Tiamat that produces self-contained (onedir) packages. I’m not going to repeat the official guides that explain it in details. Instead I’ll try to highlight several important things and describe a bit how it works under the hood.

Now to the technical details:

New SEPs

These SEPs are not tied to the Salt Phosphorus release, but happened around the same time and are quite important.

Environments

Saltenv support is somewhat convoluted and has many tricky corner cases. In theory, it could be solved by a proper abstraction layer, but it would be a huge undertaking. For now, it seems that a more pragmatic approach is to focus on the corner cases:

Salt SSH

Internal refactorings

Memory leaks

A couple of non-obvious memory leaks were fixed, and it resulted in a much more predictable Salt Master memory consumption:

Salt Master memory consumption graph

Image by Sergey Kacheev

These fixes resulted in SEP-65 (also see #62191), which proposes deprecating the __utils__ dunder.

Windows

Pillar

Proxy minions

A couple of fixes to stabilize the DeltaProxy code:

Restconf proxy

The restconf modules (execution, state, proxy) are based on the RESTCONF protocol and can be used to manage network devices like Cisco, Juniper, Huawei, etc. that support it. The proxy module requires device credentials to be defined in a pillar.

Available execution module functions:

Available state module functions:

PR #58965 by Jamie (Bear) Murphy

Clouds

Operating systems

Ansible improvements

A couple of improvements in the ansible module that allows running Ansible playbooks via Salt:

PRs #60056 and #60983 by Pablo Suárez Hernández

Nifty tricks

Autodetect module.run syntax

The use_superseded: [module.run] option is no longer needed (and taken into account) - you can mix and match both styles in a single state or orchestration file:

old_style_echo:
  module.run:
    - name: test.echo
    - text: Hello there!

new_style_echo:
  module.run:
    - test.echo:
      - Hello there!

old_style_arg:
  module.run:
    - name: test.arg
    - args:
      - arg1
      - arg2
    - kwargs:
        kw1: kwarg1
        kw2: kwarg2

new_style_arg:
  module.run:
    - test.arg:
        - arg1
        - arg2
        - kw1: kwarg1
        - kw2: kwarg2
# salt-call state.apply module_run_example

local:
----------
          ID: old_style_echo
    Function: module.run
        Name: test.echo
      Result: True
     Comment: Module function test.echo executed
     Started: 06:14:05.062176
    Duration: 3.677 ms
     Changes:
              ----------
              ret:
                  Hello there!
----------
          ID: new_style_echo
    Function: module.run
      Result: True
     Comment: test.echo: Hello there!
     Started: 06:14:05.066995
    Duration: 2.261 ms
     Changes:
              ----------
              test.echo:
                  Hello there!
----------
          ID: old_style_arg
    Function: module.run
        Name: test.arg
      Result: True
     Comment: Module function test.arg executed
     Started: 06:14:05.069479
    Duration: 2.073 ms
     Changes:
              ----------
              ret:
                  ----------
                  args:
                      - arg1
                      - arg2
                  kwargs:
                      ----------
                      kw1:
                          kwarg1
                      kw2:
                          kwarg2
----------
          ID: new_style_arg
    Function: module.run
      Result: True
     Comment: test.arg: Success
     Started: 06:14:05.071975
    Duration: 2.2 ms
     Changes:
              ----------
              test.arg:
                  ----------
                  args:
                      - arg1
                      - arg2
                  kwargs:
                      ----------
                      kw1:
                          kwarg1
                      kw2:
                          kwarg2

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

With that said, I recommend always using the new syntax. Having a CI check that is triggered by the old syntax will help transition to the new one. But because salt-lint does not have a such check, you can use the following command (e.g. with a highstate):

salt-call state.apply module_run_example test=true -l debug 2>&1 | grep 'legacy module.run syntax'

[DEBUG   ] Detected legacy module.run syntax: old_style_echo
[DEBUG   ] Detected legacy module.run syntax: old_style_arg

PR #61772 by Daniel Wozniak

Success/failure percentages

When the master/minion state_output_pct option is set to true, a summary will show successful and failed operations in a state run as a percentage of total operations:

Summary for local
------------
Succeeded: 4 (changed=4)
Failed:    0
Success %: 100.0
Failure %: 0.0
------------
Total states run:     4
Total run time:   4.275 ms

PR #60991 by Nicholas Hughes

Roll-up duplicate state IDs

The state_compress_ids master/minion option will roll an output of the following state block into two sections instead of four (esp. useful with state_output: terse_id):

mix-matched results:
  cmd.run:
    - names:
      - "true"
      - "false"
      - "/bin/true"
      - "/bin/false"

Before:

salt-call state.apply phosphorus.rollup -l critical

local:
  Name: mix-matched results - Function: cmd.run - Result: Changed Started: - 05:58:23.470018 Duration: 5.567 ms
  Name: mix-matched results - Function: cmd.run - Result: Failed Started: - 05:58:23.475857 Duration: 5.276 ms
  Name: mix-matched results - Function: cmd.run - Result: Changed Started: - 05:58:23.481379 Duration: 5.02 ms
  Name: mix-matched results - Function: cmd.run - Result: Failed Started: - 05:58:23.486660 Duration: 5.434 ms

Summary for local
------------
Succeeded: 2 (changed=4)
Failed:    2
Success %: 50.0
Failure %: 50.0
------------
Total states run:     4
Total run time:  21.297 ms

After:

salt-call state.apply phosphorus.rollup -l critical

local:
  Name: mix-matched results (2) - Function: cmd.run - Result: Changed Started: - 05:59:26.910423 Duration: 11.555 ms
  Name: mix-matched results (2) - Function: cmd.run - Result: Failed Started: - 05:59:26.916231 Duration: 12.202 ms

Summary for local
------------
Succeeded: 2 (changed=4)
Failed:    2
Success %: 50.0
Failure %: 50.0
------------
Total states run:     4
Total run time:  23.757 ms

When enabled, this option consolidates the state data by ID and result (e.g. success or failure). The earliest start time is chosen for display, duration is aggregated, and the total number of names is shown in parentheses to the right of the ID.

PR #61550 by Nicholas Hughes

Etag support

Several HTTP-capable file functions in Salt (file.managed, archive.extracted, etc) can generate unnecessary traffic by downloading files each time a state is applied. A couple of proposals were discussed in the past, see #38971, #43518, #45314, and #47962.

One of the standard methods to avoid that is to use server-side change detection based on Etag. This HTTP response header is an identifier for a specific version of a resource. It lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content was not changed.

This is exactly what was implemented:

Refer to your web-server docs on how to enable Etag support if it is not enabled by default.

New Jinja filters

A couple of new Jinja filters, mostly from itertools:

I wish Jinja had list comprehensions

PRs #61503, #61833, #62227, and #62373 by Nicholas Hughes

Modify schedules while minion is not running

Implement the ability to add, delete, purge, and modify Salt scheduler jobs when the Salt minion (event bus) is not running. Now you can use salt-call --local schedule.add ... to modify the /etc/salt/minion.d/_schedule.conf.

PR #61423 by Gareth J. Greenaway

freezer.compare function

The freezer module was introduced in Salt 3000 Neon and can freeze and restore package sets. The new freezer.compare function will display the difference between two frozen states. The results are shown as a dictionary with keys for packages and repositories. Each key may contain a changes dictionary showing items that differ between the two frozen states. Items shown in the “old” changes but not the “new” were removed. Items in “new” but not “old” were added. Items shown in both probably updated/changed versions between freezes.

Here is a little demo:

salt-call freezer.freeze old
local:
    True

apt install cowsay

salt-call freezer.freeze new
local:
    True

salt-call freezer.compare old new
local:
    ----------
    pkgs:
        ----------
        new:
            ----------
            cowsay:
                3.03+dfsg2-7
    repos:
        ----------

PR #61682 by Nicholas Hughes

grains_refresh_pre_exec

Environments using dynamic grains from external sources often have a need to populate grains information in another tool (such as a DCIM) and have those grains immediately take effect during the next execution on a minion. With the current workflow, grains can only be refreshed manually, on an interval (causing potentially unnecessary polling), or after a highstate (when we wanted the grains refreshed before the highstate).

The grains_refresh_pre_exec setting (False by default) allows for a minion to check its grains prior to the execution of any operation to see if they have changed and, if so, to inform the master of the new grains.

PR #61709 by Nicholas Hughes

Jinja error context

Ever seen the dreaded jinja2.exceptions.UndefinedError: 'dict object' has no attribute 'foo' while being unable to find what particular dict object caused the error? Now it will be obvious:

local:
    Data failed to compile:
----------
    Rendering SLS 'base:phosphorus.foo' failed: Jinja variable 'dict object' has no attribute 'foo'; line 6

---
{% set myobj = {'baz': 'qux'} %}

foo:
  test.configurable_test_state:
    - changes: True
    - comment: {{ myobj['foo'] }}    <======================
---

PR #61553 by Justin Findlay

YAML linting

A simple module based on yamllint to validate Salt state files. Here is an example:

foo:
  test.configurable_test_state:
     - changes: True
    - comment: A comment
pip install yamllint
salt-call yaml.lint salt://phosphorus/foo.sls

local:
    ----------
    problems:
        |_
          ----------
          column:
              6
          comment:
              wrong indentation: expected 4 but found 5 (indentation)
          level:
              warning
          line:
              3
        |_
          ----------
          column:
              5
          comment:
              syntax error: expected <block end>, but found '<block sequence start>' (syntax)
          level:
              error
          line:
              4
    source:
        foo:
          test.configurable_test_state:
             - changes: True
            - comment: A comment

By default, it expects a plain yaml file, but can optionally pass the file through a renderer system if you specify the pre_render=jinja argument. The linter config is hardcoded and reflects the Salt yaml style as close as possible. In future versions, it might be extended to support custom yamllint configs as well.

PRs #61183 and #62243 by Thomas Phipps

Dpkg notify plugin

This is a dpkg plugin developed by SUSE to send notifications when new packages are installed outside of Salt. It complements the existing yumnotify and zyppernotify plugins, plus an upcoming dnfnotify plugin.

It is not a part of the Salt package, so to use it:

  1. Install as described in the README
  2. Grab the pkgset beacon from Uyuni Project, put it into the _beacons dir then run salt-call saltutil.sync_beacons
  3. Add the following snippet to the minion config (or minion’s pillar) and restart the minion
beacons:
  pkgset:
    - interval: 5

Then, when you install or remove any package manually (using apt-get), the minion will emit an event like this:

salt/beacon/minion/pkgset/changed	{
    "_stamp": "2022-07-18T08:20:38.979858",
    "id": "minion"
}

There is no information in the event that mentions what package was installed. To determine that you can use the pkg.list_pkgs function or the freezer module.

PR #61180 by Victor Zhestkov

Other notable features

You can find other changes and bugfixes in the official CHANGELOG.md and Release Notes