Search code examples
salt-project

Call a salt state from another salt state


Somewhat new to salt here. I set up salt and managed to get everything working rather nicely. After the setup, I decided to try make small state files and run those from another state file. The main reason being ease of troubleshooting/changing a small file vs. troubleshooting a huge state file. Unfortunately, outside of the top file, I haven't been successful in getting a state to be called from another state.

For example, let's say I have foo.sls and bar.sls, and bar.sls is a state that installs packages properly. I have tried the following.

#foo.sls
packages:
  state.apply:
    - source: salt://packages/bar.sls

Also

#foo.sls
packages/bar.sls:
  state.apply

And also

#foo.sls
state.apply:
  - source: salt://packages/bar.sls

And few others that I'm not remembering right now.

Most times I've tried though, I get an error stating that state.apply is not available, leading me to believe this is either not possible, or I'm going about it wrong.

Can this be done? If so, how? If not, maybe I'll file a feature request for this, as it seems like it could be useful.


Solution

  • background

    It sounds like your issue may stem from mixing state modules and execution modules when you are writing your states.

    Brief recap, "states" are the declarative files you write (foo.sls, bar.sls), "state modules" are the directives you list inside those states (e.g. pkg.installed), and "execution modules" provide the commands that salt actually knows how to run (state.apply, test.ping, etc.).

    state.apply is simply the execution module that knows how to interpret states. It may help to note that the fully qualified name of state.apply from the docs (or if you browse the salt source tree) is actually salt.modules.state.apply, whereas pkg.installed is salt.states.pkg.installed. A module in the modules namespace generally cannot be accessed from states namespace and vice versa, though there are exceptions. Knowing the full namespace is also a necessary distinction when an execution module and a state modules share a virtual name, e.g. test exists as both salt.modules.test and salt.states.test.

    solution

    If I understand correctly, you probably want to include your state files within each other.

    For example, say you have the following folder structure:

    $ tree srv
    srv
    └── salt
        ├── foo.sls
        └── packages
            └── bar.sls
    

    and bar.sls has the following contents

    # bar.sls
    packages_bar_install_fun:
      pkg.installed:
        - pkgs:
          - cowsay
          - fortune
          - sl
    

    To include bar.sls into foo.sls you just need to reference it using dot notation, depending on your folder structure

    # foo.sls
    include:
      - packages.bar
    foo_another_example_state:
      test.show_notification:
        - text: |
          foo.sls can have other states inside of it,
          though you may need to use `require` if you want
          them interspersed between multiple includes 
    

    Now you can either just include - foo in your top.sls, or run salt '<tgt>' state.apply foo test=True and you should see that package.bar would also be applied.

    The salt docs also include a section titled Moving Beyond a Single SLS which discusses using include and extend to glue multiple states together.

    Splitting up an SLS for organizational purposes is also a common use for init.sls


    As a brief aside, there are some states which go the other direction and allow allow you to run execution modules from within an SLS. A few examples are salt.states.module.run and salt.states.saltmod.state, though the uses for these are far more specialized than what it seems you're trying to do.