Manual

Describing a script

Groups, variables with a shape, tags, and stages that run alone. The inventory, the group_vars and site.yml are written from what the script says it has.

A script can say what it has and what each part is for: its host groups, its tags, the variables it needs and the stages it runs in. The inventory, the group_vars and site.yml are then written from that, and a script that imports another gets all of it.

# base.py
from glom import Optional, Or
from instamix import Template, file, group, stage, tag, var

group("web", "Hosts that serve the site",
      children=["web_front"], vars={"ansible_user": "root"})
group("web_front", "The ones the load balancer sends to", hosts=["web1", "web2"])

var("site_name", "The name the site answers to", group="web")
var("site_port", "The port nginx listens on", Or(int, Template), default=8080, group="web")
var("site_ntp", "Where the time comes from",
    [{"hostname": str, Optional("pool"): bool}], default=[], group="web")

tag("site_reload", "Only reload nginx")

stage("base", "Prepare the hosts", file("base.yml", """
    - name: Prepare the hosts
      hosts: web
      ...
"""))

Every one takes a name and a description, and the description is required.

Groups#

group(name, description, hosts=, children=, vars=) describes a host group. children nests groups; hosts is a list of names or {name: {inventory variables}}. Describing a group again, in another script, adds its hosts, children and vars to it. The groups become the inventory (an INI file with the descriptions as comments), and their vars go into group_vars/<group>.yml.

Variables#

var(name, description, shape=str, default=, group="all") describes a variable. The shape is what glom’s Match takes: a type, a list or dict of shapes, Or(...), Optional(key) for a key that may be missing.

  • A list of a shape, [str], is zero or more of it; NonEmpty([str]) is one or more, and NonEmpty(str) a string with something in it.
  • Template stands for a value that ansible templates, so Or(int, Template) is a number or a template for one.
  • A default goes into the group’s group_vars, under its description.
  • Without one the variable is needed: group(vars=...) of that group, or of one it is nested in, has to set it, here or in a script that imports this one. optional=True lets it stay unset.

Tags and stages#

tag(name, description) describes a tag that the playbooks use.

stage(name, description, playbook, when=, tags=) describes a stage: a playbook the script declares (default NAME.yml) that does one thing. site.yml imports the stages in the order they were described, each under a tag of its name. when is a condition on the hosts’ variables, for a stage that a variable switches off.

Stitching scripts together#

# site.py
import base                      # its groups, variables and stages come first
from instamix import file, group, stage, var

group("web", "Where the app runs", vars={"site_name": "example.com"})
var("app_enabled", "Whether the app is installed", bool, default=True, group="web")
stage("app", "Install the app", file("app.yml", "..."), when="app_enabled | bool")

base.py still runs by itself when it sets what it needs. python site.py describe lists the whole; describe --json writes it as one JSON object on one line ({"stages": [...], "tags": [...], "groups": [...], "variables": [...]}), and describe --jsonl as an event for each:

$ python site.py describe
Stages (run one with --tags):
  base  Prepare the hosts
  app   Install the app  [when: app_enabled | bool]

Groups:
  web          Hosts that serve the site
    web_front  The ones the load balancer sends to  (web1, web2)

Variables of web:
  site_name: str (needed)
      The name the site answers to
  site_port: Or(int, Template) = 8080
      The port nginx listens on

Running one stage#

$ python site.py run --tags app --limit web1     # one stage, one host
$ python site.py run --skip-tags base
$ python site.py run -e app_enabled=false        # a variable switches a stage off

run refuses a tag the script does not describe (ansible itself would run nothing and say nothing), and it refuses to start when a needed variable is not set or a value has not its shape. Only the values the script gives are checked: what -e or another inventory brings is not.

lint checks the same, and that every tag the playbooks use is described (always and never are ansible’s own), that every stage has its playbook and that every nested group is described.

The source of this page

    Type to search. ↑ ↓ to move, enter to open.