Inverting States#

In SLS function refs present and absent are complimentary to each other, and are used to ensure that a resource (corresponding to a state) gets created or gets deleted. Sometimes it is desirable to not write two different SLS files for just creating and deleting some states. This is where SLS inversion tries to help. Command line argument --invert can be used to invert the behaviour of SLS file. However, this is not without limitations.

Motivation#

The goal of SLS inversion is to use same SLS file to both create and delete resources as the case may be. All this can be understood with help of an example:

Assure Resource Group Present test_group:
  azure.resource_management.resource_groups.present:
  - resource_group_name: test_group
  - parameters:
      location: eastus

In the above SLS, we are creating a resource group. In the usual case to delete the above resource group we need to create a SLS file like so:

Assure Resource Group absent test_group:
  azure.resource_management.resource_groups.absent:
  - resource_group_name: test_group
  - parameters:
      location: eastus

With help of command-line parameter --invert we can create and delte the resource group using the same SLS, like so:

Click here to see command execution details
$ tail rg_create.sls
Assure Resource Group Present test_group:
azure.resource_management.resource_groups.present:
- resource_group_name: test_group
- parameters:
    location: eastus

$ idem state --output json rg_create.sls
{
    "azure.resource_management.resource_groups_|-Assure Resource Group Present test_group_|-Assure Resource Group Present test_group_|-present": {
        "changes": {
            "new": {
                "id": "/subscriptions/some-subscription/resourceGroups/test_group",
                "name": "test_group",
                "type": "Microsoft.Resources/resourceGroups",
                "location": "eastus",
                "properties": {
                    "provisioningState": "Succeeded"
                }
            }
        },
        "comment": "Created",
        "name": "Assure Resource Group Present test_group",
        "result": true,
        "old_state": null,
        "new_state": null,
        "__run_num": 1
    }
}
$ idem state --output json --invert rg_create.sls
{
    "azure.resource_management.resource_groups_|-Assure Resource Group Present test_group_|-Assure Resource Group Present test_group_|-absent": {
        "changes": {
            "old": {
                "id": "/subscriptions/some-subscription/resourceGroups/test_group",
                "name": "test_group",
                "type": "Microsoft.Resources/resourceGroups",
                "location": "eastus",
                "properties": {
                    "provisioningState": "Succeeded"
                }
            }
        },
        "comment": "Accepted",
        "name": "Assure Resource Group Present test_group",
        "result": true,
        "old_state": null,
        "new_state": null,
        "__run_num": 1
    }
}

In this manner we can reverse changes done by an existing SLS file without actually writing a separate SLS file.

State Requisite Handling#

With SLS inversion, all state requisites also get inverted, in a sense that the order of execution of states is reversed. The idea behind this approach is to execute states in an inverted SLS in the reverse order of normal SLS. For example consider the following SLS:

sleep_mid:
  time.sleep:
  - require:
    - time: sleep_first
  - duration: 1

sleep_end:
  time.sleep:
  - require:
    - time: sleep_mid
  - duration: 1

sleep_independent:
  time.sleep:
  - duration: 1

sleep_first:
  time.sleep:
  - duration: 1

Normal Run#

In a normal run (without --invert) the order of execution will be

  1. sleep_first, sleep_independent

  2. sleep_mid

  3. sleep_end

Click here to see actual execution details
$ idem state --output json invert.sls
{
    "time_|-sleep_independent_|-sleep_independent_|-sleep": {
        "comment": [
            "Successfully slept for 1 seconds."
        ],
        "old_state": {},
        "new_state": {},
        "name": "sleep_independent",
        "result": true,
        "__run_num": 1
    },
    "time_|-sleep_first_|-sleep_first_|-sleep": {
        "comment": [
            "Successfully slept for 1 seconds."
        ],
        "old_state": {},
        "new_state": {},
        "name": "sleep_first",
        "result": true,
        "__run_num": 2
    },
    "time_|-sleep_mid_|-sleep_mid_|-sleep": {
        "comment": [
            "Successfully slept for 1 seconds."
        ],
        "old_state": {},
        "new_state": {},
        "name": "sleep_mid",
        "result": true,
        "__run_num": 3
    },
    "time_|-sleep_end_|-sleep_end_|-sleep": {
        "comment": [
            "Successfully slept for 1 seconds."
        ],
        "old_state": {},
        "new_state": {},
        "name": "sleep_end",
        "result": true,
        "__run_num": 4
    }
}

Inverted Run#

With a --invert command-line parameter the order of state execution will be:

  1. sleep_end, sleep_independent

  2. sleep_mid

  3. sleep_first

Click here to see actual execution details
$ idem state --output json --invert invert.sls
{
    "time_|-sleep_end_|-sleep_end_|-sleep": {
        "comment": [
            "Successfully slept for 1 seconds."
        ],
        "old_state": {},
        "new_state": {},
        "name": "sleep_end",
        "result": true,
        "__run_num": 1
    },
    "time_|-sleep_independent_|-sleep_independent_|-sleep": {
        "comment": [
            "Successfully slept for 1 seconds."
        ],
        "old_state": {},
        "new_state": {},
        "name": "sleep_independent",
        "result": true,
        "__run_num": 2
    },
    "time_|-sleep_mid_|-sleep_mid_|-sleep": {
        "comment": [
            "Successfully slept for 1 seconds."
        ],
        "old_state": {},
        "new_state": {},
        "name": "sleep_mid",
        "result": true,
        "__run_num": 3
    },
    "time_|-sleep_first_|-sleep_first_|-sleep": {
        "comment": [
            "Successfully slept for 1 seconds."
        ],
        "old_state": {},
        "new_state": {},
        "name": "sleep_first",
        "result": true,
        "__run_num": 4
    }
}

Requirements#

To make SLS inversion work, all mandatory parameters required for absent and present for any given state should be present in the SLS, irrespective of actual function ref you are using. For example, the SLS file

Delete {{subnet}}:
  aws.ec2.subnet.absent:
  - name: {{VpcName}}

will not work with --invert command-line parameter. Since some mandatory parameters required by present are not provided. If the parameters required by present are also provided like below, inversion will work as expected with or without command-line parameter --invert.

Delete {{subnet}}:
  aws.ec2.subnet.absent:
  - name: {{VpcName}}
  - vpc_id: {{VpcId}}
  - cidr_block: 10.0.0.0/24
  - availability_zone: us-east-1d
  - tags:
    - Key: Name
      Value: one1

Limitations#

For some use cases, --invert works well, but there are limitations.

Argument binding does not work

Because argument binding uses output of one state to define input of another state, it doesn’t work with SLS inversion.