pyrtan / examples / site-status / site-status.toml
2,167 bytes · last changed 2026-10-26 in Watches and growing from a gap: trees that run when something speaks, and the branch a gap asks for drafted by rules; release 0.9.0
| 1 | # Is a site up? The host comes from the sentence, so it cannot be checked |
| 2 | # at load time; the runtime refuses anything outside `hosts`, and this tree |
| 3 | # allows any host on purpose, with the wildcard spelled out. |
| 4 | |
| 5 | name = "site-status" |
| 6 | version = 2 |
| 7 | namespace = "pyrtan" |
| 8 | description = "Whether a web site answers, and how quickly." |
| 9 | tags = ["ops", "read-only"] |
| 10 | license = "MIT" |
| 11 | hosts = ["*"] |
| 12 | start = "probe" |
| 13 | |
| 14 | [[triggers]] |
| 15 | pattern = "is {host:host} up" |
| 16 | [[triggers]] |
| 17 | pattern = "is {host:host} down" |
| 18 | [[triggers]] |
| 19 | pattern = "[check] status of {host:host}" |
| 20 | |
| 21 | [inputs.host] |
| 22 | type = "str" |
| 23 | pattern = "(?i)^[a-z0-9.\\-]+(:[0-9]+)?$" |
| 24 | description = "A host name, with no scheme" |
| 25 | |
| 26 | # On its own, every quarter hour, for one host; `pyrtan watches` shows the |
| 27 | # last run and whether the path changed since the run before (up -> down). |
| 28 | [[watches]] |
| 29 | name = "example.com" |
| 30 | every = "15m" |
| 31 | inputs = { host = "example.com" } |
| 32 | |
| 33 | [nodes.probe] |
| 34 | kind = "observe" |
| 35 | http = { method = "GET", url = "https://{{ inputs.host | lower }}/" } |
| 36 | into = "resp" |
| 37 | timeout = 8 |
| 38 | retries = 1 |
| 39 | next = "status" |
| 40 | |
| 41 | [nodes.status] |
| 42 | kind = "decide" |
| 43 | on = "resp.status" |
| 44 | branches = [ |
| 45 | { when = { between = [200, 399] }, then = "up" }, |
| 46 | { when = { between = [400, 499] }, then = "refusing" }, |
| 47 | { when = { gte = 500 }, then = "erroring" }, |
| 48 | { else = "odd" }, |
| 49 | ] |
| 50 | |
| 51 | [nodes.up] |
| 52 | kind = "done" |
| 53 | say = "{{ inputs.host }} is up: {{ state.resp.status }} in {{ state.resp.elapsed_ms }} ms." |
| 54 | output = { host = "{{ inputs.host }}", up = true, status = "{{ state.resp.status }}", elapsed_ms = "{{ state.resp.elapsed_ms }}" } |
| 55 | |
| 56 | [nodes.refusing] |
| 57 | kind = "done" |
| 58 | say = "{{ inputs.host }} answers but refuses the front page ({{ state.resp.status }}); the server is up." |
| 59 | output = { host = "{{ inputs.host }}", up = true, status = "{{ state.resp.status }}", note = "client error on /" } |
| 60 | |
| 61 | [nodes.erroring] |
| 62 | kind = "fail" |
| 63 | reason = "server-error" |
| 64 | say = "{{ inputs.host }} is answering {{ state.resp.status }}: up, but broken." |
| 65 | output = { host = "{{ inputs.host }}", up = false, status = "{{ state.resp.status }}" } |
| 66 | |
| 67 | [nodes.odd] |
| 68 | kind = "fail" |
| 69 | reason = "unexpected-status" |
| 70 | say = "{{ inputs.host }} answered {{ state.resp.status }}, which this tree has no branch for." |