Skip to content

JSON output

plan, apply and drift print a single JSON document with --json, instead of the human-readable report. It contains everything the text report shows: each resource's status and changes, the notifications, the summary counts, and the exit status. Nothing else is written to standard output, so it can be redirected to a file or piped into jq. Errors that stop sadame before a report exists (a bad option, a recipe that does not load) go to standard error, and standard output stays empty.

Example

sadame drift --json on the machine from Plan, apply, drift, after break-things.sh ran:

$ sudo sadame drift examples/10-drift-demo --json
{
  "format_version": 1,
  "sadame_version": "0.5.1",
  "command": "drift",
  "host": "local",
  "platform": {
    "name": "Debian GNU/Linux 12 (bookworm)",
    "family": "debian",
    "package_manager": "apt"
  },
  "tags": [],
  "skip_tags": [],
  "resources": [
    {
      "id": "package[nginx]",
      "type": "package",
      "name": "nginx",
      "status": "unchanged",
      "changes": [],
      "notifications": [],
      "error": null
    },
    {
      "id": "user[demo-app]",
      "type": "user",
      "name": "demo-app",
      "status": "unchanged",
      "changes": [],
      "notifications": [],
      "error": null
    },
    {
      "id": "directory[/etc/demo-app]",
      "type": "directory",
      "name": "/etc/demo-app",
      "status": "unchanged",
      "changes": [],
      "notifications": [],
      "error": null
    },
    {
      "id": "file[/etc/demo-app/app.conf]",
      "type": "file",
      "name": "/etc/demo-app/app.conf",
      "status": "update",
      "changes": [
        {
          "attribute": "mode",
          "from": "0644",
          "to": "0640"
        },
        {
          "attribute": "content",
          "sensitive": false,
          "diff": "--- current\n+++ desired\n@@ -1,4 +1,4 @@\n # Managed by sadame. Local changes will be overwritten.\n listen    = 127.0.0.1:9000\n-workers   = 16\n+workers   = 4\n log_level = info\n"
        }
      ],
      "notifications": [],
      "error": null
    },
    {
      "id": "service[nginx]",
      "type": "service",
      "name": "nginx",
      "status": "update",
      "changes": [
        {
          "attribute": "state",
          "from": "stopped",
          "to": "running"
        }
      ],
      "notifications": [],
      "error": null
    }
  ],
  "notifications": [],
  "summary": {
    "create": 0,
    "update": 2,
    "remove": 0,
    "run": 0,
    "unchanged": 3,
    "failed": 0
  },
  "drift": true,
  "failed": false,
  "exit_status": 1,
  "duration_seconds": 0.01
}

Top level

Field Type Description
format_version integer Version of this format, currently 1. It changes when a field is removed or changes meaning; new fields may be added without a change.
sadame_version string Version of sadame that wrote the report.
command string "plan", "apply" or "drift".
host string The machine the report is about; "local" for the machine sadame runs on.
platform object See platform.
tags, skip_tags array of strings The --tags and --skip-tags of the run; empty without them.
resources array One resource object per declared resource, in recipe order; with --tags or --skip-tags, per selected resource.
notifications array Notification runs during apply, in the order they ran. Always empty for plan and drift.
summary object Number of resources per status; see statuses. Every status of the command is present, including those with 0.
drift boolean or null plan and drift: true when at least one resource differs from the recipe. apply: null.
failed boolean true when a resource failed, or for apply, a notification failed.
exit_status integer The exit status of the command: see Plan, apply, drift.
duration_seconds number Wall-clock time of the run, rounded to hundredths of a second.

platform

Field Type Description
name string PRETTY_NAME from /etc/os-release, or "macOS".
family string or null "debian", "rhel", "alpine", "arch", or null when unknown.
package_manager string or null "apt", "dnf", "apk", "pacman", or null.

resource

Field Type Description
id string type[name], as in the text output and in notifies:.
type string "package", "file", "directory", "service", "exec" or "user".
name string The resource name: package name, path, command, user name.
status string See statuses.
changes array Change objects. Empty when the resource is unchanged, failed before comparing, or skipped.
notifications array plan and drift: what this resource would trigger. apply: what it triggered. Each has action, source, target and timing ("delayed" or "immediately").
error string or null Why the resource failed, or null.

change

An attribute that differs. Most changes have from and to:

Field Type Description
attribute string "state", "mode", "owner", "group", "enabled", "uid", "home", "shell", "groups" or "command".
from string, integer, boolean, array or null Current value; null when the resource does not exist yet.
to same Desired value.
reason string Only on exec changes: why the command would run, for example "not_if exited non-zero".

File content changes carry a diff instead of the full text:

Field Type Description
attribute string "content"
sensitive boolean true when the file has sensitive: true.
diff string or null Unified diff from current to desired, or null when sensitive.

notification run

Field Type Description
action string The action, such as "reload" or "run".
source string Id of the resource whose change triggered it.
target string Id of the resource the action ran on.
timing string "delayed" or "immediately".
status string "ok" or "failed".
error string or null Why it failed, or null.

Statuses

Command Status Meaning
plan, drift create Does not exist yet.
update Exists, some attributes differ.
remove Exists, the recipe says state: :absent.
run An exec whose guards say it must run.
unchanged Matches the recipe.
failed The current state could not be read; see error.
apply changed Was changed.
unchanged Already matched; nothing was done.
failed Changing it failed; see error.
skipped Not looked at, because an earlier resource or immediate notification failed.

Push mode

When sadame runs on remote hosts (-h, -g, --limit), --json prints one document for the whole run. Each host's report, as described above, is nested under hosts:

Field Type Description
format_version integer As above.
sadame_version string Version of sadame on the machine that ran the push.
command string "plan", "apply", "drift" or "facts".
hosts array One object per host, in the order they were selected.
hosts[].name string The host's name from the inventory or -h.
hosts[].status string "ok", "drift", "failed", "error", "unreachable" or "skipped"; see Remote hosts.
hosts[].exit_status integer or null That host's exit status; 2 when it could not be reached or prepared; null when it was skipped.
hosts[].error string or null Why the host could not be reached or prepared, or sadame's error message.
hosts[].report object or null The host's report; null when sadame did not run there.
exit_status integer The highest exit status of all hosts: the run's exit status.
duration_seconds number Wall-clock time of the whole run.

For example, a drift check on one web server after its configuration was edited by hand:

$ sadame drift site.rb -h web2.example.com --json
{
  "format_version": 1,
  "sadame_version": "0.5.1",
  "command": "drift",
  "hosts": [
    {
      "name": "web2.example.com",
      "status": "drift",
      "exit_status": 1,
      "error": null,
      "report": {
        "format_version": 1,
        "sadame_version": "0.5.1",
        "command": "drift",
        "host": "web2.example.com",
        "platform": {
          "name": "Debian GNU/Linux 12 (bookworm)",
          "family": "debian",
          "package_manager": "apt"
        },
        "tags": [],
        "skip_tags": [],
        "resources": [
          {
            "id": "file[/etc/motd]",
            "type": "file",
            "name": "/etc/motd",
            "status": "unchanged",
            "changes": [],
            "notifications": [],
            "error": null
          },
          {
            "id": "package[nginx]",
            "type": "package",
            "name": "nginx",
            "status": "unchanged",
            "changes": [],
            "notifications": [],
            "error": null
          },
          {
            "id": "directory[/etc/demo-app]",
            "type": "directory",
            "name": "/etc/demo-app",
            "status": "unchanged",
            "changes": [],
            "notifications": [],
            "error": null
          },
          {
            "id": "file[/etc/demo-app/web.conf]",
            "type": "file",
            "name": "/etc/demo-app/web.conf",
            "status": "update",
            "changes": [
              {
                "attribute": "content",
                "sensitive": false,
                "diff": "--- current\n+++ desired\n@@ -1,3 +1,3 @@\n # Managed by sadame.\n environment = production\n-workers     = 12\n+workers     = 8\n"
              }
            ],
            "notifications": [],
            "error": null
          },
          {
            "id": "file[/etc/nginx/conf.d/demo-app.conf]",
            "type": "file",
            "name": "/etc/nginx/conf.d/demo-app.conf",
            "status": "unchanged",
            "changes": [],
            "notifications": [],
            "error": null
          },
          {
            "id": "service[nginx]",
            "type": "service",
            "name": "nginx",
            "status": "unchanged",
            "changes": [],
            "notifications": [],
            "error": null
          }
        ],
        "notifications": [],
        "summary": {
          "create": 0,
          "update": 1,
          "remove": 0,
          "run": 0,
          "unchanged": 5,
          "failed": 0
        },
        "drift": true,
        "failed": false,
        "exit_status": 1,
        "duration_seconds": 0.01
      }
    }
  ],
  "exit_status": 1,
  "duration_seconds": 0.22
}

validate

sadame validate --json prints format_version, sadame_version, command ("validate"), recipe, platform, exit_status, and hosts: one object per host with name, status ("ok" or "error"), resources (how many the recipe declared; null on error) and error.

facts

sadame facts --json prints one document per machine. With -h, -g or --limit, each host's document is the report of the push-mode document below.

Field Type Description
format_version integer As above.
sadame_version string Version of sadame that read the facts.
command string "facts".
host string The host's name, as in a report.
platform object See platform.
facts object Every fact by name, as in Facts; null for what could not be read.

Using it with jq

Resources that differ, from a saved drift report:

jq -r '.resources[] | select(.status != "unchanged") | "\(.id): \(.status)"' drift.json
file[/etc/demo-app/app.conf]: update
service[nginx]: update

Only the counts:

jq '.summary' drift.json
{
  "create": 0,
  "update": 2,
  "remove": 0,
  "run": 0,
  "unchanged": 3,
  "failed": 0
}

Common mistakes

  • Reading failed as "drift found". failed means a resource could not be checked or changed. Drift is the drift field, and the exit status 1.
  • Parsing the text output. Its layout is for people and may change; the JSON fields are documented here and versioned by format_version.
  • Mixing standard error into the file. > drift.json 2>&1 or &> drift.json puts error messages into the JSON file. Redirect only standard output: > drift.json.

See also