neulab/tldr huggingface dataset:
cache_dir (bool) This setting requires the software to be stored on the file_roots and only applies to URLs that begin with salt://. If True the entire directory where the installer resides will be recursively cached. This is useful for installers that depend on other files in the same directory for installation.
WARNING: Be aware that all files and directories in the same location as the installer file will be copied down to the minion. If you place your software definition file in the root of winrepo (/srv/salt/win/repo-ng) and it contains cache_dir: True the entire contents of winrepo will be cached to the minion. Therefore, it is best practice to place your installer files in a subdirectory if they are to be stored in winrepo.
Here's an example using cache_dir:
sqlexpress: '12.0.2000.8': full_name: Microsoft SQL Server 2014 Setup (English) installer: 'salt://win/repo/sqlexpress/setup.exe' install_flags: '/ACTION=install /IACCEPTSQLSERVERLICENSETERMS /Q' cache_dir: True
cache_file (str) This setting requires the file to be stored on the file_roots and only applies to URLs that begin with salt://. It indicates a single file to copy down for use with the installer. It is copied to the same location as the installer. Use this over cache_dir if there are many files in the directory and you only need a specific file and don't want to cache additional files that may reside in the installer directory.
use_scheduler (bool) If set to True, Windows will use the task scheduler to run the installation.  A one-time task will be created in the task scheduler and launched. The return to the minion will be that the task was launched successfully, not that the software was installed successfully.
NOTE: This is used by the software definition for Salt itself. The first thing the Salt installer does is kill the Salt service, which then kills all child processes. If the Salt installer is launched via Salt, then the installer itself is killed leaving Salt on the machine but not running. Use of the task scheduler allows an external process to launch the Salt installation so its processes aren't killed when the Salt service is stopped.
source_hash (str) This tells Salt to compare a hash sum of the installer to the provided hash sum before execution. The value can be formatted as <hash_algorithm>=<hash_sum>, or it can be a URI to a file containing the hash sum.
For a list of supported algorithms, see the hashlib documentation.
Here's an example using source_hash:
messageanalyzer: '4.0.7551.0': full_name: 'Microsoft Message Analyzer' installer: 'salt://win/repo/messageanalyzer/MessageAnalyzer64.msi' install_flags: '/quiet /norestart' uninstaller: '{1CC02C23-8FCD-487E-860C-311EC0A0C933}' uninstall_flags: '/quiet /norestart' msiexec: True source_hash: 'sha1=62875ff451f13b10a8ff988f2943e76a4735d3d4'
Not Implemented The following parameters are often seen in the software definition files hosted on the Git repo. However, they are not implemented and have no effect on the installation process.
param bool reboot Not implemented
param str locale Not implemented
Managing Windows Software on a Standalone Windows Minion The Windows Software Repository functions similarly in a standalone environment, with a few differences in the configuration.
To replace the winrepo runner that is used on the Salt master, an execution module exists to provide the same functionality to standalone minions. The functions are named the same as the ones in the runner, and are used in the same way; the only difference is that salt-call is used instead of salt-run:
salt-call winrepo.update_git_repos salt-call pkg.refresh_db
After executing the previous commands the repository on the standalone system is ready for use.
Troubleshooting My software installs correctly but pkg.installed says it failed If you have a package that seems to install properly, but Salt reports a failure then it is likely you have a version or full_name mismatch.
Check the exact full_name and version as shown in Add/Remove Programs (appwiz.cpl). Use pkg.list_pkgs to check that the full_name and version exactly match what is installed. Make sure the software definition file has the exact value for full_name and that the version matches exactly.
Also, make sure the version is wrapped in single quotes in the software definition file.
Changes to sls files not being picked up You may have recently updated some of the software definition files on the repo.  Ensure you have refreshed the database on the minion.
salt winminion pkg.refresh_db
How Success and Failure are Reported by pkg.installed The install state/module function of the Windows package manager works roughly as follows:
1. Execute pkg.list_pkgs to get a list of software currently on the machine
2. Compare the requested version with the installed version
3. If versions are the same, report no changes needed
4. Install the software as described in the software definition file
5. Execute pkg.list_pkgs to get a new list of software currently on the machine
6. Compare the requested version with the new installed version
7. If versions are the same, report success
8. If versions are different, report failure
Winrepo Upgrade Issues To minimize potential issues, it is a good idea to remove any winrepo git repositories that were checked out by the legacy (pre-2015.8.0) winrepo code when upgrading the master to 2015.8.0 or later. Run winrepo.update_git_repos to clone them anew after the master is started.
pygit2/GitPython Support for Maintaining Git Repos The winrepo.update_git_repos runner now makes use of the same underlying code used by the Git Fileserver Backend and Git External Pillar to maintain and update its local clones of git repositories. If a compatible version of either pygit2 (0.20.3 and later) or GitPython (0.3.0 or later) is installed, Salt will use it instead of the old method (which invokes the git.latest state).
NOTE: If compatible versions of both pygit2 and GitPython are installed, then Salt will prefer pygit2. To override this behavior use the winrepo_provider configuration parameter:
The winrepo execution module (discussed above in the Managing Windows Software on a Standalone Windows Minion section) does not yet officially support the new pygit2/GitPython functionality, but if either pygit2 or GitPython is installed into Salt's bundled Python then it should work. However, it should be considered experimental at this time.
Accessing Authenticated Git Repos (pygit2) Support for pygit2 added the ability to access authenticated git repositories and to set per-remote config settings. An example of this would be the following:
winrepo_remotes: - https://github.com/saltstack/salt-winrepo.git - git@github.com:myuser/myrepo.git: - pubkey: /path/to/key.pub - privkey: /path/to/key - passphrase: myaw3s0m3pa$$phr4$3 - https://github.com/myuser/privaterepo.git: - user: mygithubuser - password: CorrectHorseBatteryStaple
NOTE: Per-remote configuration settings work in the same fashion as they do in gitfs, with global parameters being overridden by their per-remote counterparts. For instance, setting winrepo_passphrase would set a global passphrase for winrepo that would apply to all SSH-based remotes, unless overridden by a passphrase per-remote parameter.
See here for more a more in-depth explanation of how per-remote configuration works in gitfs. The same principles apply to winrepo.
Maintaining Git Repos A clean argument has been added to the winrepo.update_git_repos runner. When clean is True it will tell the runner to dispose of directories under the winrepo_dir_ng/winrepo_dir_ng which are not explicitly configured. This prevents the need to manually remove these directories when a repo is removed from the config file. To clean these old directories, just pass clean=True:
salt-run winrepo.update_git_repos clean=True
If a mix of git and non-git Windows Repo definition files are being used, then this should not be used, as it will remove the directories containing non-git definitions.
Name Collisions Between Repos Collisions between repo names are now detected. The winrepo.update_git_repos runner will not proceed if any are detected. Consider the following configuration:
winrepo_remotes: - https://foo.com/bar/baz.git - https://mydomain.tld/baz.git - https://github.com/foobar/baz
The winrepo.update_git_repos runner will refuse to update repos here, as all three of these repos would be checked out to the same directory. To work around this, a per-remote parameter called name can be used to resolve these conflicts:
winrepo_remotes: - https://foo.com/bar/baz.git - https://mydomain.tld/baz.git: - name: baz_junior - https://github.com/foobar/baz: - name: baz_the_third
Legacy Minions The Windows Package Manager was upgraded with breaking changes starting with Salt 2015.8.0. To maintain backwards compatibility Salt continues to support older minions.
The breaking change was to generate the winrepo database on the minion instead of the master. This allowed for the use of Jinja in the software definition files. It enabled the use of pillar, grains, execution modules, etc. during compile time. To support this new functionality, a next-generation (ng) repo was created.
See the Changes in Version 2015.8.0 for details.
On prior versions of Salt, or legacy minions, the winrepo database was generated on the master and pushed down to the minions. Any grains exposed at compile time would have been those of the master and not the minion.
The repository for legacy minions is named salt-winrepo and is located at:
• https://github.com/saltstack/salt-winrepo
Legacy Configuration Winrepo settings were changed with the introduction of the Next Generation (ng) of winrepo.
Legacy Master Config Options There were three options available for a legacy master to configure winrepo.  Unless you're running a legacy master as well, you shouldn't need to configure any of these.
• win_gitrepos
• win_repo
• win_repo_mastercachefile
win_gitrepos: (list)
A list of URLs to github repos. Default is a list with a single URL:
• 'https://github.com/saltstack/salt-winrepo.git'
win_repo: (str)
The location on the master to store the winrepo. The default is /srv/salt/win/repo.
win_repo_mastercachefile: (str) The location on the master to generate the winrepo database file. The default is /srv/salt/win/repo/winrep.p
Legacy Minion Config Options There is only one option available to configure a legacy minion for winrepo.
• win_repo_cachefile
win_repo_cachefile: (str)
The location on the Salt file server to obtain the winrepo database file. The default is salt://win/repo/winrepo.p
NOTE: If the location of the winrepo.p file is not in the default location on the master, the win_repo_cachefile setting will need to be updated to reflect the proper location on each minion.
Legacy Quick Start You can get up and running with winrepo pretty quickly just using the defaults.  Assuming no changes to the default configuration (ie, file_roots) run the following commands on the master:
salt-run winrepo.update_git_repos salt-run winrepo.genrepo salt * pkg.refresh_db salt * pkg.install firefox
These commands clone the default winrepo from github, generate the metadata file, push the metadata file down to the legacy minion, and install the latest version of Firefox.
Legacy Initialization Initializing the winrepo for a legacy minion is similar to that for a newer minion. There is an added step in that the metadata file needs to be generated on the master prior to refreshing the database on the minion.
Populate the Local Repository The SLS files used to install Windows packages are not distributed by default with Salt. So, the first step is to clone the repo to the master. Use the winrepo.update_git_repos runner initialize the repository in the location specified by winrepo_dir in the master config. This will pull the software definition files down from the git repository.
Generate the Metadata File The next step is to create the metadata file for the repo (winrepo.p). The metadata file is generated on the master using the winrepo.genrepo runner.
NOTE: You only need to do this if you need to support legacy minions.
Update the Minion Database Run pkg.refresh_db on each of your Windows minions to copy the metadata file down to the minion.
Changes in Version 2015.8.0+ Git repository management for the Windows Software Repository changed in version 2015.8.0, and several master/minion config parameters were renamed for consistency.
For a complete list of the new winrepo config options, see here for master config options, and here for configuration options for masterless Windows minions.
pygit2/GitPython Support On the master, the winrepo.update_git_repos runner was updated to use either pygit2 or GitPython to checkout the git repositories containing repo data. If pygit2 or GitPython is installed, existing winrepo git checkouts should be removed after upgrading to 2015.8.0.  Then they should be cloned again by running winrepo.update_git_repos.
If neither GitPython nor pygit2 are installed, Salt will fall back to pre-existing behavior for winrepo.update_git_repos, and a warning will be logged in the master log.
NOTE: Standalone Windows minions do not support the new GitPython/pygit2 functionality, and will instead use the git.latest state to keep repositories up-to-date. More information on how to use the Windows Software Repo on a standalone minion can be found here.
Config Parameters Renamed Many of the legacy winrepo configuration parameters changed in version 2015.8.0 to make them more consistent. Below are the parameters which changed for version 2015.8.0:
Master Config
┌─────────────────────────┬──────────────────────────┐ │Old Name                 │ New Name                 │ ├─────────────────────────┼──────────────────────────┤ │win_repo                 │ winrepo_dir              │ ├─────────────────────────┼──────────────────────────┤ │win_repo_mastercachefile │ No longer used on master │ ├─────────────────────────┼──────────────────────────┤ │win_gitrepos             │ winrepo_remotes          │ └─────────────────────────┴──────────────────────────┘
NOTE: The winrepo_dir_ng and winrepo_remotes_ng settings were introduced in 2015.8.0 for working with the next generation repo.
See here for detailed information on all master config options for the Windows Repo.
Minion Config
┌───────────────────┬───────────────────┐ │Old Name           │ New Name          │ ├───────────────────┼───────────────────┤ │win_repo           │ winrepo_dir       │ ├───────────────────┼───────────────────┤ │win_repo_cachefile │ winrepo_cachefile │ ├───────────────────┼───────────────────┤ │win_gitrepos       │ winrepo_remotes   │ └───────────────────┴───────────────────┘
See here for detailed information on all minion config options for the Windows Repo.
Windows-specific Behaviour Salt is capable of managing Windows systems, however due to various differences between the operating systems, there are some things you need to keep in mind.
This document will contain any quirks that apply across Salt or generally across multiple module functions. Any Windows-specific behavior for particular module functions will be documented in the module function documentation. Therefore this document should be read in conjunction with the module function documentation.
Group parameter for files Salt was originally written for managing Unix-based systems, and therefore the file module functions were designed around that security model. Rather than trying to shoehorn that model on to Windows, Salt ignores these parameters and makes non-applicable module functions unavailable instead.
One of the commonly ignored parameters is the group parameter for managing files. Under Windows, while files do have a 'primary group' property, this is rarely used.  It generally has no bearing on permissions unless intentionally configured and is most commonly used to provide Unix compatibility (e.g.  Services For Unix, NFS services).
Because of this, any file module functions that typically require a group, do not under Windows. Attempts to directly use file module functions that operate on the group (e.g. file.chgrp) will return a pseudo-value and cause a log message to appear. No group parameters will be acted on.
If you do want to access and change the 'primary group' property and understand the implications, use the file.get_pgid or file.get_pgroup functions or the pgroup parameter on the file.chown module function.
Dealing with case-insensitive but case-preserving names Windows is case-insensitive, but however preserves the case of names and it is this preserved form that is returned from system functions. This causes some issues with Salt because it assumes case-sensitive names. These issues generally occur in the state functions and can cause bizarre looking errors.
To avoid such issues, always pretend Windows is case-sensitive and use the right case for names, e.g. specify user=Administrator instead of user=administrator.
Follow issue #11801 for any changes to this behavior.
Dealing with various username forms Salt does not understand the various forms that Windows usernames can come in, e.g. username, mydomain\username, username@mydomain.tld can all refer to the same user. In fact, Salt generally only considers the raw username value, i.e.  the username without the domain or host information.
Using these alternative forms will likely confuse Salt and cause odd errors to happen. Use only the raw username value in the correct case to avoid problems.
Specifying the None group Each Windows system has built-in _None_ group. This is the default 'primary group' for files for users not on a domain environment.
Unfortunately, the word _None_ has special meaning in Python - it is a special value indicating 'nothing', similar to null or nil in other languages.
To specify the None group, it must be specified in quotes, e.g.  ./salt '*' file.chpgrp C:\path\to\file "'None'".
Symbolic link loops Under Windows, if any symbolic link loops are detected or if there are too many levels of symlinks (defaults to 64), an error is always raised.
For some functions, this behavior is different to the behavior on Unix platforms. In general, avoid symlink loops on either platform.
Overview In its most typical use, Salt is a software application in which clients, called "minions" can be commanded and controlled from a central command server called a "master".
Commands are normally issued to the minions (via the master) by calling a client script simply called, 'salt'.
Salt features a pluggable transport system to issue commands from a master to minions. The default transport is ZeroMQ.
Salt Client Overview The salt client is run on the same machine as the Salt Master and communicates with the salt-master to issue commands and to receive the results and display them to the user.
The primary abstraction for the salt client is called 'LocalClient'.
When LocalClient wants to publish a command to minions, it connects to the master by issuing a request to the master's ReqServer (TCP: 4506)
The LocalClient system listens to responses for its requests by listening to the master event bus publisher (master_event_pub.ipc).
Salt Master Overview The salt-master daemon runs on the designated Salt master and performs functions such as authenticating minions, sending, and receiving requests from connected minions and sending and receiving requests and replies to the 'salt' CLI.
Moving Pieces When a Salt master starts up, a number of processes are started, all of which are called 'salt-master' in a process-list but have various role categories.
Among those categories are:
• Publisher
• EventPublisher
• MWorker
Publisher The Publisher process is responsible for sending commands over the designated transport to connected minions. The Publisher is bound to the following:
• TCP: port 4505
• IPC: publish_pull.ipc
Each salt minion establishes a connection to the master Publisher.
EventPublisher The EventPublisher publishes master events out to any event listeners. It is bound to the following:
• IPC: master_event_pull.ipc
• IPC: master_event_pub.ipc
MWorker Worker processes manage the back-end operations for the Salt Master.
The number of workers is equivalent to the number of 'worker_threads' specified in the master configuration and is always at least one.
Workers are bound to the following:
• IPC: workers.ipc
ReqServer The Salt request server takes requests and distributes them to available MWorker processes for processing. It also receives replies back from minions.
The ReqServer is bound to the following:
• TCP: 4506
Each salt minion establishes a connection to the master ReqServer.
Job Flow The Salt master works by always publishing commands to all connected minions and the minions decide if the command is meant for them by checking themselves against the command target.
The typical lifecycle of a salt job from the perspective of the master might be as follows:
1.  A command is issued on the CLI. For example, 'salt my_minion test.version'.
2.  The 'salt' command uses LocalClient to generate a request to the salt master by connecting to the ReqServer on TCP:4506 and issuing the job.
3.  The salt-master ReqServer sees the request and passes it to an available MWorker over workers.ipc.
4.  A worker picks up the request and handles it. First, it checks to ensure that the requested user has permissions to issue the command. Then, it sends the publish command to all connected minions. For the curious, this happens in ClearFuncs.publish().
5.  The worker announces on the master event bus that it is about to publish a job to connected minions. This happens by placing the event on the master event bus (master_event_pull.ipc) where the EventPublisher picks it up and distributes it to all connected event listeners on master_event_pub.ipc.
6.  The message to the minions is encrypted and sent to the Publisher via IPC on publish_pull.ipc.
7.  Connected minions have a TCP session established with the Publisher on TCP port 4505 where they await commands. When the Publisher receives the job over publish_pull, it sends the jobs across the wire to the minions for processing.
8.  After the minions receive the request, they decrypt it and perform any requested work, if they determine that they are targeted to do so.
9.  When the minion is ready to respond, it publishes the result of its job back to the master by sending the encrypted result back to the master on TCP 4506 where it is again picked up by the ReqServer and forwarded to an available MWorker for processing. (Again, this happens by passing this message across workers.ipc to an available worker.)
10. When the MWorker receives the job it decrypts it and fires an event onto the master event bus (master_event_pull.ipc). (Again for the curious, this happens in AESFuncs._return().
11. The EventPublisher sees this event and re-publishes it on the bus to all connected listeners of the master event bus (on master_event_pub.ipc). This is where the LocalClient has been waiting, listening to the event bus for minion replies. It gathers the job and stores the result.
12. When all targeted minions have replied or the timeout has been exceeded, the salt client displays the results of the job to the user on the CLI.
Salt Minion Overview The salt-minion is a single process that sits on machines to be managed by Salt. It can either operate as a stand-alone daemon which accepts commands locally via 'salt-call' or it can connect back to a master and receive commands remotely.
When starting up, salt minions connect back to a master defined in the minion config file. They connect to two ports on the master:
TCP: 4505 This is the connection to the master Publisher. It is on this port that the minion receives jobs from the master.
TCP: 4506 This is the connection to the master ReqServer. It is on this port that the minion sends job results back to the master.
Event System Similar to the master, a salt-minion has its own event system that operates over IPC by default. The minion event system operates on a push/pull system with IPC files at minion_event_<unique_id>_pub.ipc and minion_event_<unique_id>_pull.ipc.
The astute reader might ask why have an event bus at all with a single-process daemon. The answer is that the salt-minion may fork other processes as required to do the work without blocking the main salt-minion process and this necessitates a mechanism by which those processes can communicate with each other. Secondarily, this provides a bus by which any user with sufficient permissions can read or write to the bus as a common interface with the salt minion.
Minion Job Flow When a salt minion starts up, it attempts to connect to the Publisher and the ReqServer on the salt master. It then attempts to authenticate and once the minion has successfully authenticated, it simply listens for jobs.
Jobs normally come either come from the 'salt-call' script run by a local user on the salt minion or they can come directly from a master.
The job flow on a minion, coming from the master via a 'salt' command is as follows:
1) A master publishes a job that is received by a minion as outlined by the master's job flow above.  2) The minion is polling its receive socket that's connected to the master Publisher (TCP 4505 on master). When it detects an incoming message, it picks it up from the socket and decrypts it.  3) A new minion process or thread is created and provided with the contents of the decrypted message. The _thread_return() method is provided with the contents of the received message.  4) The new minion thread is created. The _thread_return() function starts up and actually calls out to the requested function contained in the job.  5) The requested function runs and returns a result. [Still in thread.]  6) The result of the function that's run is encrypted and returned to the master's ReqServer (TCP 4506 on master). [Still in thread.]  7) Thread exits. Because the main thread was only blocked for the time that it took to initialize the worker thread, many other requests could have been received and processed during this time.
A Note on ClearFuncs vs. AESFuncs A common source of confusion is determining when messages are passed in the clear and when they are passed using encryption. There are two rules governing this behaviour:
1) ClearFuncs is used for intra-master communication and during the initial authentication handshake between a minion and master during the key exchange.  2) AESFuncs is used everywhere else.
Changelog With the addition of SEP 01 the keepachangelog format was introduced into our CHANGELOG.md file. The Salt project is using the towncrier tool to manage the CHANGELOG.md file. The reason this tool was added to manage the changelog was because we were previously managing the file manually and it would cause many merge conflicts. This tool allows us to add changelog entries into separate files and before a release we simply need to run towncrier --version=<version> for it to compile the changelog correctly.
How do I add a changelog entry To add a changelog entry you will need to add a file in the changelog directory.  The file name should follow the syntax <issue #>.<type>. If it is a security fix then the following syntax will need to be used cve-<cve-number>.security.
The types are in alignment with keepachangelog:
removed: any features that have been removed
deprecated: any features that will soon be removed
changed: any changes in current existing features
fixed: any bug fixes
added: any new features added
security: any fixes for a cve
For example if you are fixing a bug for issue number #1234 your filename would look like this: changelog/1234.fixed. The contents of the file should contain a summary of what you are fixing. If there is a legitimate reason to not include an issue number with a given contribution you can add the PR number as the file name (<PR #>.<type>).
For a security fix your filename would look like this: changelog/cve-2021-25283.security.
If your PR does not align with any of the types, then you do not need to add a changelog entry.
How to generate the changelog This step is only used when we need to generate the changelog right before releasing.  You should NOT run towncrier on your PR, unless you are preparing the final PR to update the changelog before a release.
You can run the towncrier tool directly or you can use nox to help run the command and ensure towncrier is installed in a virtual environment. The instructions below will detail both approaches.
If you want to see what output towncrier will produce before generating the change log you can run towncrier in draft mode:
towncrier --draft --version=3001
nox -e 'changelog(draft=True)' -- 3000.1
Version will need to be set to whichever version we are about to release. Once you are confident the draft output looks correct you can now generate the changelog by running:
towncrier --version=3001
nox -e 'changelog(draft=False)' -- 3000.1
After this is run towncrier will automatically remove all the files in the changelog directory.
Contributing So you want to contribute to the Salt project? Excellent! You can help in a number of ways:
• Using Salt and opening well-written bug reports.
• Joining a working group.
• Answering questions on irc, the community Slack, the salt-users mailing list, Server Fault, or r/saltstack on Reddit.
• Fixing bugs.
• Enhancing the documentation.
• Providing workarounds, patches, or other code without tests.
• Telling other people about problems you solved using Salt.
If you’d like to update docs or fix an issue, you’re going to need the Salt repo. The best way to contribute is using Git.
Environment Setup To hack on Salt or the docs you’re going to need to set up your development environment. If you already have a workflow that you’re comfortable with, you can use that, but otherwise this is an opinionated guide for setting up your dev environment. Follow these steps and you’ll end out with a functioning dev environment and be able to submit your first PR.
This guide assumes at least a passing familiarity with Git, a common version control tool used across many open source projects, and is necessary for contributing to Salt. For an introduction to Git, watch Salt Docs Clinic - Git For the True Beginner.  Because of its widespread use, there are many resources for learning more about Git. One popular resource is the free online book Learn Git in a Month of Lunches.
pyenv, Virtual Environments, and You We recommend pyenv, since it allows installing multiple different Python versions, which is important for testing Salt across all the versions of Python that we support.
On Linux Install pyenv:
git clone https://github.com/pyenv/pyenv.git ~/.pyenv export PATH="$HOME/.pyenv/bin:$PATH" git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv
On Mac Install pyenv using brew:
brew update brew install pyenv brew install pyenv-virtualenv
Now add pyenv to your .bashrc:
echo 'export PATH="$HOME/.pyenv/bin:$PATH"' >> ~/.bashrc pyenv init 2>> ~/.bashrc pyenv virtualenv-init 2>> ~/.bashrc
For other shells, see the pyenv instructions.
Go ahead and restart your shell. Now you should be able to install a new version of Python:
pyenv install 3.7.0
If that fails, don’t panic! You’re probably just missing some build dependencies. Check out pyenv common build problems.
Now that you’ve got your version of Python installed, you can create a new virtual environment with this command:
pyenv virtualenv 3.7.0 salt
Then activate it:
pyenv activate salt
Sweet! Now you’re ready to clone Salt so you can start hacking away! If you get stuck at any point, check out the resources at the beginning of this guide. IRC and Slack are particularly helpful places to go.
Get The Source! Salt uses the fork and clone workflow for Git contributions. See Using the Fork-and-Branch Git Workflow for how to implement it. But if you just want to hurry and get started you can go ahead and follow these steps:
Clones are so shallow. Well, this one is anyway:
git clone --depth=1 --origin salt https://github.com/saltstack/salt.git
This creates a shallow clone of Salt, which should be fast. Most of the time that’s all you’ll need, and you can start building out other commits as you go. If you really want all 108,300+ commits you can just run git fetch --unshallow. Then go make a sandwich because it’s gonna be a while.
You’re also going to want to head over to GitHub and create your own fork of Salt. Once you’ve got that set up you can add it as a remote:
git remote add yourname <YOUR SALT REMOTE>
If you use your name to refer to your fork, and salt to refer to the official Salt repo you’ll never get upstream or origin confused.
NOTE: Each time you start work on a new issue you should fetch the most recent changes from salt/upstream.
pre-commit and nox Setup Here at Salt we use pre-commit and nox to make it easier for contributors to get quick feedback, for quality control, and to increase the chance that your merge request will get reviewed and merged. Nox enables us to run multiple different test configurations, as well as other common tasks. You can think of it as Make with superpowers.  Pre-commit does what it sounds like: it configures some Git pre-commit hooks to run black for formatting, isort for keeping our imports sorted, and pylint to catch issues like unused imports, among others. You can easily install them in your virtualenv with:
python -m pip install pre-commit nox pre-commit install
WARNING: Currently there is an issue with the pip-tools-compile pre-commit hook on Windows.  The details around this issue are included here: https://github.com/saltstack/salt/issues/56642.  Please ensure you export SKIP=pip-tools-compile to skip pip-tools-compile.
Now before each commit, it will ensure that your code at least looks right before you open a pull request. And with that step, it’s time to start hacking on Salt!
imagemagick Setup One last prerequisite is to have imagemagick installed, as it is required by Sphinx for generating the HTML documentation.
# On Mac, via homebrew brew install imagemagick
# Example Linux installation: Debian-based sudo apt install imagemagick
Salt Issues Create Your Own Perhaps you’ve come to this guide because you found a problem in Salt, and you’ve diagnosed the cause. Maybe you need some help figuring out the problem. In any case, creating quality bug reports is a great way to contribute to Salt even if you lack the skills, time, or inclination to fix it yourself. If that’s the case, head on over to Salt’s issue tracker on GitHub.
Creating a good report can take a little bit of time - but every minute you invest in making it easier for others to reproduce and understand your issue is time well spent. The faster someone can understand your issue, the faster it will be able to get fixed correctly.
The thing that every issue needs goes by many names, but one at least as good as any other is MCVE - Minimum Complete Verifiable Example.
In a nutshell:
• Minimum: All of the extra information has been removed. Will 2 or 3 lines of master/minion config still exhibit the behavior?
• Complete: Minimum also means complete. If your example is missing information, then it’s not complete. Salt, Python, and OS versions are all bits of information that make your example complete. Have you provided the commands that you ran?
• Verifiable: Can someone take your report and reproduce it?
Slow is smooth, and smooth is fast - it may feel like you’re taking a long time to create your issue if you’re creating a proper MCVE, but a MCVE eliminates back and forth required to reproduce/verify the issue so someone can actually create a fix.
Pick An Issue If you don’t already have an issue in mind, you can search for help wanted issues. If you also search for good first issue then you should be able to find some issues that are good for getting started contributing to Salt. Documentation issues are also good starter issues. When you find an issue that catches your eye (or one of your own), it’s a good idea to comment on the issue and mention that you’re working on it. Good communication is key to collaboration - so if you don’t have time to complete work on the issue, just leaving some information about when you expect to pick things up again is a great idea!
Hacking Away Salt, Tests, Documentation, and You Before approving code contributions, Salt requires:
• documentation
• meaningful passing tests
• correct code
Documentation fixes just require correct documentation.
What If I Don’t Write Tests or Docs? If you aren’t into writing documentation or tests, we still welcome your contributions! But your PR will be labeled Needs Testcase and Help Wanted until someone can get to write the tests/documentation.  Of course, if you have a desire but just lack the skill we are more than happy to collaborate and help out! There’s the documentation working group and the testing working group.  We also regularly stream our test clinic live on Twitch every Tuesday afternoon and Thursday morning, Central Time. If you’d like specific help with tests, bring them to the clinic. If no community members need help, you can also just watch tests written in real time.
Documentation Salt uses both docstrings, as well as normal reStructuredText files in the salt/doc folder for documentation. Sphinx is used to generate the documentation, and does require setting up imagemagick on your OS.  Since we use nox, you can build your docs and view them in your browser with this one-liner:
python -m nox -e 'docs-html(compress=False, clean=False)'; cd doc/_build/html; python -m webbrowser http://localhost:8000/contents.html; python -m http.server
The first time this will take a while because there are a lot of modules. Maybe you should go grab some dessert if you already finished that sandwich. But once Sphinx is done building the docs, python should launch your default browser with the URL http://localhost:8000/contents.html. Now you can navigate to your docs and ensure your changes exist. If you make changes, you can simply run this:
cd -; python -m nox -e 'docs-html(compress=False, clean=False)'; cd doc/_build/html; python -m http.server
And then refresh your browser to get your updated docs. This one should be quite a bit faster since Sphinx won’t need to rebuild everything.
If your change is a docs-only change, you can go ahead and commit/push your code and open a PR. You can indicate that it’s a docs-only change by adding [Documentation] to the title of your PR. Otherwise you’ll want to write some tests and code.
Running Development Salt Note: If you run into any issues in this section, check the Troubleshooting section.
If you’re going to hack on the Salt codebase you’re going to want to be able to run Salt locally. The first thing you need to do is install Salt as an editable pip install:
python -m pip install -e .
This will let you make changes to Salt without having to re-install it.
After all of the dependencies and Salt are installed, it’s time to set up the config for development. Typically Salt runs as root, but you can specify which user to run as. To configure that, just copy the master and minion configs. We have .gitignore setup to ignore the local/ directory, so we can put all of our personal files there.
mkdir -p local/etc/salt/
Create a master config file as local/etc/salt/master:
cat <<EOF >local/etc/salt/master user: $(whoami) root_dir: $PWD/local/ publish_port: 55505 ret_port: 55506 EOF
And a minion config as local/etc/salt/minion:
cat <<EOF >local/etc/salt/minion user: $(whoami) root_dir: $PWD/local/ master: localhost id: saltdev master_port: 55506 EOF
Now you can start your Salt master and minion, specifying the config dir.
salt-master --config-dir=local/etc/salt/ --log-level=debug --daemon salt-minion --config-dir=local/etc/salt/ --log-level=debug --daemon
Now you should be able to accept the minion key:
salt-key -c local/etc/salt -Ay
And check that your master/minion are communicating:
salt -c local/etc/salt \* test.version
Rather than running test.version from your master, you can run it from the minion instead:
salt-call -c local/etc/salt test.version
Note that you’re running salt-call instead of salt, and you’re not specifying the minion (\*), but if you’re running the dev version then you still will need to pass in the config dir. Now that you’ve got Salt running, you can hack away on the Salt codebase!
If you need to restart Salt for some reason, if you’ve made changes and they don’t appear to be reflected, this is one option:
kill -INT $(pgrep salt-master) kill -INT $(pgrep salt-minion)
If you’d rather not use kill, you can have a couple of terminals open with your salt virtualenv activated and omit the --daemon argument. Salt will run in the foreground, so you can just use ctrl+c to quit.
Test First? Test Last? Test Meaningfully! You can write tests first or tests last, as long as your tests are meaningful and complete! Typically the best tests for Salt are going to be unit tests. Testing is a whole topic on its own, But you may also want to write functional or integration tests. You’ll find those in the salt/tests directory.
When you’re thinking about tests to write, the most important thing to keep in mind is, “What, exactly, am I testing?” When a test fails, you should know:
• What, specifically, failed?
• Why did it fail?
• As much as possible, what do I need to do to fix this failure?
If you can’t answer those questions then you might need to refactor your tests.
When you’re running tests locally, you should make sure that if you remove your code changes your tests are failing. If your tests aren’t failing when you haven’t yet made changes, then it’s possible that you’re testing the wrong thing.
But whether you adhere to TDD/BDD, or you write your code first and your tests last, ensure that your tests are meaningful.
Running Tests As previously mentioned, we use nox, and that’s how we run our tests. You should have it installed by this point but if not you can install it with this:
python -m pip install nox
Now you can run your tests:
python -m nox -e "pytest-3.7(coverage=False)" -- tests/unit/cli/test_batch.py
It’s a good idea to install espeak or use say on Mac if you’re running some long-running tests. You can do something like this:
python -m nox -e "pytest-3.7(coverage=False)" -- tests/unit/cli/test_batch.py; espeak "Tests done, woohoo!"
That way you don’t have to keep monitoring the actual test run.
Changelog and Commit! When you write your commit message you should use imperative style. Do this: Add frobnosticate capability
Don’t do this: Added frobnosticate capability
But that advice is backwards for the changelog. We follow the keepachangelog approach for our changelog, and use towncrier to generate it for each release. As a contributor, all that means is that you need to add a file to the salt/changelog directory, using the <issue #>.<type> format. For instanch, if you fixed issue 123, you would do:
echo "Made sys.doc inform when no minions return" > changelog/123.fixed
And that’s all that would go into your file. When it comes to your commit message, it’s usually a good idea to add other information, such as
• What does a reviewer need to know about the change that you made?
• If someone isn’t an expert in this area, what will they need to know?
This will also help you out, because when you go to create the PR it will automatically insert the body of your commit messages.
PR Time! Once you’ve done all your dev work and tested locally, you should check out our PR guidelines.  After you read that page, it’s time to open a new PR. Fill out the PR template - you should have updated or created any necessary docs, and written tests if you’re providing a code change. When you submit your PR, we have a suite of tests that will run across different platforms to help ensure that no known bugs were introduced.
Now What? You’ve made your changes, added documentation, opened your PR, and have passing tests… now what? When can you expect your code to be merged?
When you open your PR, a reviewer will get automatically assigned. If your PR is submitted during the week you should be able to expect some kind of communication within that business day. If your tests are passing and we’re not in a code freeze, ideally your code will be merged that day. If you haven’t heard from your assigned reviewer, ping them on GitHub, irc, or Community Slack.
It’s likely that your reviewer will leave some comments that need addressing - it may be a style change, or you forgot a changelog entry, or need to update the docs. Maybe it’s something more fundamental - perhaps you encountered the rare case where your PR has a much larger scope than initially assumed.
Whatever the case, simply make the requested changes (or discuss why the requests are incorrect), and push up your new commits. If your PR is open for a significant period of time it may be worth rebasing your changes on the most recent changes to Salt. If you need help, the previously linked Git resources will be valuable.
But if, for whatever reason, you’re not interested in driving your PR to completion then just note that in your PR. Something like, “I’m not interested in writing docs/tests, I just wanted to provide this fix - someone else will need to complete this PR.” If you do that then we’ll add a “Help Wanted” label and someone will be able to pick up the PR, make the required changes, and it can eventually get merged in.
In any case, now that you have a PR open, congrats! You’re a Salt developer! You rock!
Troubleshooting zmq.core.error.ZMQError Once the minion starts, you may see an error like the following:
:: zmq.core.error.ZMQError: ipc path "/path/to/your/virtualenv/var/run/salt/minion/minion_event_7824dcbcfd7a8f6755939af70b96249f_pub.ipc" is longer than 107 characters (sizeof(sockaddr_un.sun_path)).
This means that the path to the socket the minion is using is too long. This is a system limitation, so the only workaround is to reduce the length of this path. This can be done in a couple different ways:
1. Create your virtualenv in a path that is short enough.
2. Edit the :conf_minion:sock_dir minion config variable and reduce its length. Remember that this path is relative to the value you set in :conf_minion:root_dir.
NOTE: The socket path is limited to 107 characters on Solaris and Linux, and 103 characters on BSD-based systems.
No permissions to access … If you forget to pass your config path to any of the salt* commands, you might see
No permissions to access "/var/log/salt/master", are you running as the correct user?
Just pass -c local/etc/salt (or whatever you named it)
File descriptor limit You might need to raise your file descriptor limit. You can check it with:
ulimit -n
If the value is less than 3072, you should increase it with:
ulimit -n 3072 # For c-shell: limit descriptors 3072
Pygit2 or other dependency install fails You may see some failure messages when installing requirements. You can directly access your nox environment and possibly install pygit (or other dependency) that way. When you run nox, you’ll see a message like this:
nox > Re-using existing virtual environment at .nox/pytest-parametrized-3-crypto-none-transport-zeromq-coverage-false.
For this, you would be able to install with:
.nox/pytest-parametrized-3-crypto-none-transport-zeromq-coverage-false/bin/python -m pip install pygit2
Deprecating Code Salt should remain backwards compatible, though sometimes, this backwards compatibility needs to be broken because a specific feature and/or solution is no longer necessary or required.  At first one might think, let me change this code, it seems that it's not used anywhere else so it should be safe to remove.  Then, once there's a new release, users complain about functionality which was removed and they where using it, etc. This should, at all costs, be avoided, and, in these cases, that specific code should be deprecated.
In order to give users enough time to migrate from the old code behavior to the new behavior, the deprecation time frame should be carefully determined based on the significance and complexity of the changes required by the user.
Salt feature releases are based on the Periodic Table. Any new features going into the master branch will be named after the next element in the Periodic Table. For example, Magnesium was the feature release name associated with the v3002 tag. At that point in time, any new features going into the master branch, after v3002 was tagged, were part of the Aluminium feature release.
A deprecation warning should be in place for at least two major releases before the deprecated code and its accompanying deprecation warning are removed.  More time should be given for more complex changes.  For example, if the current release under development is 3001, the deprecated code and associated warnings should remain in place and warn for at least Aluminium.