SLS Structure#

Idem utilize a system called SLS - Structured Layered States. The SLS system allows for a specific data structure that represents the desired state of a system. That target data structure can be obtained through a layered rendering process. Hence the name - Structured Layered State.

This allows for data to be represented in any way imaginable - JSON, YAML, XML, or even programming languages. This major benefit makes it easy to write Idem code in whatever way works best for you!

Core Components#

This document is all about defining the core components of the SLS file, that way you can identify what the underlying data structure looks like and how to best get there. By default SLS files are represented as YAML, unlike other YAML systems you may be familiar with, the SLS format has a finite dept, making it very easy to learn, read, and write.

The first components we will discuss are the ID Declaration, Path Reference, and Arguments. The core use of all SLS files can be encapsulated inside these three simple components:

Some_Desired_State:  # ID Declaration
  cloud.instance.present:  # Path Reference
    - option: value  # Argument

ID Declaration#

The ID Declaration defines the top level identifier user for all reverences under it. The ID Declaration is also passed to the state function as the name, unless an argument is passed as name under the Path Reference.

Path Reference#

The Path Reference specifies what underlying function is being called to enforce the idempotent state for the target cloud/API/system. The dot delimited Path Reference links directly to how plugins are loaded into Idem using POP. The Path Reference is a literal reference to a location on the hub inside of Idem. The hub contains all of the code that Idem runs, therefore the Path Reference is a literal path to the code location translated as hub.idem.states.<Path Reference>.

The main benefit here is that the Path Reference gives you a direct insight into where the code that is being called resides. This makes development and debugging very simple. If a code issue exists with a state in Idem then you will know just where to find it!

Path Components#

The path is broken up into two components, the state ref, and the function ref. The periods delineate the two references. Everything after the last period is the function ref and everything before the last period is the state ref.

For instance, if the Path Ref is cloud.network.present, then the state ref is cloud.network and the function ref is present.

Arguments#

Since the Path Reference is a path to a function, the arguments are - for the most part - arguments to that function! This technically makes Idem self documenting. But some arguments are global to all state definitions, such as the name, requisites, and order options.

Name and Names#

Every state can take on a name argument, the name argument is always the primary identifier for a state. If the name argument is not provided, Idem will use the ID Declaration as the name.

The names argument allows for state replication to easily take place for multiple components. Using names can make it easy to define multiple identical resources in a clean way. Just as the names option and pass a list of desired names. Then Idem will compile the names down to multiple identical enforcements.

Some_machines:
  cloud.instance.present:
    - names:
      - web1
      - web2
      - web3
      - web4
      - web5
      - web6
      - web7
      - web8
      - web9

This state will create 9 identical cloud instances named web1 through web9

Order#

The order keyword can change the evaluation order of Idem. When Idem runs, it evaluates the statements it is given in the order they are defined in SLS files. This means that, outside of requisites, Idem will run in the order it is defined.

The order of execution can be effectively nullified when using the parallel runtime. Idem can execute using either a serial, or a parallel runtime. The parallel runtime will evaluate all requisites and then run everything that is not encumbered by a requisite at the same time. This means that if you are using the parallel runtime, the order keyword will have no effect.

If you are using the serial runtime, then each state is executed one after another. This means that the order keyword can be used to change the order in which things are executed. Use the order keyword and pass in a number. The default ordering defined by Idem will add numbers based on the highest order value passed in. This means that if you pass order: 1 then that state will be evaluated first. Similarly, you can pass order: -1 and start with negative numbers to ensure that states are executed LAST.

State_A:
  cloud.instance.present:
    - order: -1  # Make it last
State_B:
  cloud.instance.present:
    - order: 1  # Make it first

Again, remember, that if you are using the parallel runtime, then both of these instances would be created at the same time.

Requisites#

The requisite system inside of Idem is very powerful at determining the relationships that states have with each other. Being able to define requisites can make your enforcement significantly faster, more reliable, and can be used to create tasks.

These relationships are evaluated at runtime and can handle dynamic situations within your state definitions. A requisite is passed to a state as an argument:

State_A:
  cloud.instance.present:
    - require:
      - cloud.instance: State_B

State_B:
  cloud.network.present:

Is this case, State_A will only run once State_B has completed successfully. The referencing works by taking the state ref component of the path ref followed by the name or ID of the desired state to create a relationship with.

Requisite Ins#

The requisites come in two flavors - requisites, and requisite ins. Every requisite that exists can be appended with an _in to specify that the direction changes.

A standard requisite states “I require you”. For instance, this state is a standard requisite:

State_A:
  cloud.instance.present:
    - require:
      - cloud.network: State_B

State_B:
  cloud.network.present:

State_A is saying “I require State_B”

A requisite in simply says “They require me”. This means that we can get the same effect as the requisite code above with this requisite in:

State_A:
  cloud.instance.present

State_B:
  cloud.network.present:
    - require_in:
      - cloud.instance: State_A

So in this case, we are saying “I am State_B, State_A needs to require me”.

Top Level Keys#

Idem has a number of top level keys that can be used to include additional SLS files or to exclude specific IDs. You can also modify ID declarations from another file with the extend keyword.

Include#

Include simply allow you to include information from another sls file in your run. Using include is simple, at the top of your SLS file just add include followed by a list of SLS references you wish to include in your run:

include:
  - foo.bar
  - azure.networks

The include statement evaluates SLS paths. You can easily execute Idem against a single SLS file, but Idem supports having a file tree. References to locations on the file tree are dot delimited and reference directories.

For instance, the SLS file you execute is assumed to be at the root of the tree. So if you execute an SLS file called start.sls, and it has the include statement:

include:
  - aws.instances
  - azure.networks

Then Idem will look for these files in a few locations. Idem will check the aws directory for a file called instances.sls, and if it does not find that file, it will check for a directory called aws/instances/init.sls.

Note that an SLS file can have include block along with states. Here is a possible aws/instances.sls:

include:
  - .ec2.vpcs

aws.resource-1:
    aws.resource-1.present:
        ....

An SLS file aws/ec2/vpcs.sls or aws/ec2/vpcs/init.sls is expected.

Extend#

The extend keyword allows for diving into the state compiler and modifying a state from another included SLS file. This allows you to modify an external state. This can be useful if you are using your Idem code to manage multiple clouds that are NEARLY identical. So you can include the SLS files that define some external ID Declarations, then overwrite the options passed to them:

include:
  - gcp.networks

extend:
  Network_1:
    gcp.network.present:
      - ip_range: 10.10.57

In this case, the Network_1 ID from the gcp.networks SLS file will be overwritten with a new option.

RULES TO EXTEND BY#

There are a few rules to remember when extending states:

  1. Always include the SLS being extended with an include declaration

  2. Requisites (watch and require) are appended to, everything else is overwritten

  3. extend is a top level declaration, like an ID declaration, cannot be declared twice in a single SLS

  4. Many IDs can be extended under the extend declaration