13 KiB
SALTY Dog
Rule-based JSON/YAML validator using JSON schemas. Capable of filtering elements to validate partial documents, supports multiple documents per stream or file, inserting defaults, and other magic.
Getting Started
salty-dog
is distributed as a package and container, and can be installed or pulled:
> docker pull ssube/salty-dog:master
> yarn add -D salty-dog
> yarn global add salty-dog
Note: while the container is the preferred way of running salty-dog
, it has a serious limitation: docker run
combines stdout
and stderr
, making it impossible to separate logs and the output document. Writing either the logs
or dest to a file works around this.
To download, validate, and apply a Kubernetes resource:
> curl https://raw.githubusercontent.com/ssube/k8s-shards/master/roles/apps/gitlab/server/templates/ingress.yml | \
salty-dog \
--rules rules/kubernetes.yml \
--source - \
--tag kubernetes | \
kubectl apply --dry-run -f -
...
{"name":"salty-dog","hostname":"cerberus","pid":7860,"level":30,"msg":"all rules passed","time":"2019-06-16T02:04:37.797Z","v":0}
ingress.extensions/gitlab created (dry run)
Contents
Status
Releases
Build
Local Build
This project is written in Typescript and requires make
, node
, and yarn
to build.
> git clone git@github.com:ssube/salty-dog.git
> cd salty-dog
> make
After building, run with node out/index.js
or install globally with make yarn-global
.
make
targets are provided for some example arguments:
> curl https://raw.githubusercontent.com/ssube/k8s-shards/master/roles/apps/gitlab/server/templates/ingress.yml | \
make run-stream 2> >($(yarn bin)/bunyan) > >(kubectl apply --dry-run -f -)
...
[2019-06-16T03:23:56.645Z] INFO: salty-dog/8015 on cerberus: all rules passed
ingress.extensions/gitlab created (dry run)
Docker Build
This method does not require the usual dependencies to be installed, only docker
itself.
Build with Docker:
# Stretch
docker run --rm -v "$(pwd):/salty-dog" -w /salty-dog node:11-stretch make
docker build -t salty-dog:stretch -f Dockerfile.stretch .
# Alpine
docker run --rm -v "$(pwd):/salty-dog" -w /salty-dog node:11-alpine sh -c "apk add build-base && make"
docker build -t salty-dog:alpine -f Dockerfile.alpine .
Install
Docker Install
To run with Docker: docker run --rm ssube/salty-dog:master
The latest semi-stable image is ssube/salty-dog:master
. All
tags are listed here.
The Docker container is published for each branch and git tag, tagged with the git tag (or branch slug).
Rules are baked into the image in /salty-dog/rules
. To use custom rules, mount them with
-v $(pwd)/rules:/salty-dog/rules:ro
and load with --rules /rules/foo.yml
.
Yarn Install
Global
To install with Yarn as a global CLI tool: yarn global add salty-dog
To run with Node:
> export PATH="${PATH}:$(yarn global bin)"
> salty-dog --help
Project
To install with Yarn for a single project: yarn add -D salty-dog
To run with Node:
> export PATH="${PATH}:$(yarn bin)"
> salty-dog --help
Usage
Logs
salty-dog
uses node-bunyan for logging and prints JSON logs. These are not
the easiest to read, and can be pretty-printed by redirecting stderr
through bunyan
itself or jq
:
> cat resource.yml | salty-dog --rules rules/kubernetes.yml --tag kubernetes 2> >(bunyan)
...
[2019-06-15T23:53:34.223Z] INFO: salty-dog/19839 on cerberus: all rules passed
> cat resource.yml | salty-dog --rules rules/kubernetes.yml --tag kubernetes 2> >(jq)
...
{
"name": "salty-dog",
"hostname": "cerberus",
"pid": 19839,
"level": 30,
"msg": "all rules passed",
"time": "2019-06-15T23:53:34.223Z",
"v": 0
}
Using jq
allows for additional filtering and formatting. For example, >(jq 'select(.level > 30)')
will only print
warnings and errors (log level is also part of the configuration file).
To print the last line's message and error messages: >(tail -1 | jq '[.msg, ((.errors // [])[] | .msg)]')
> cat test/examples/kubernetes-resources-high.yml | salty-dog \
--rules rules/kubernetes.yml \
--tag kubernetes 2> >(tail -1 | jq '[.msg, ((.errors // [])[] | .msg)]')
[
"all rules passed"
]
> cat test/examples/kubernetes-resources-some.yml | salty-dog \
--rules rules/kubernetes.yml \
--tag kubernetes 2> >(tail -1 | jq '[.msg, ((.errors // [])[] | .msg)]')
[
"some rules failed",
".resources.limits should have required property 'memory' at $.spec.template.spec.containers[*] for kubernetes-resources",
".metadata should have required property 'labels' at $ for kubernetes-labels"
]
Modes
salty-dog
can run in a few different modes: check
mode will report errors, fix
mode will attempt to modify the
input document, and list
mode will print the active set of rules.
Check Mode
By default, salty-dog
will validate the structure and contents of the --source
document. If all rules pass, the
document will be printed to --dest
.
> cat examples/kubernetes-resources-pass.yml | salty-dog \
--rules rules/kubernetes.yml \
--tag kubernetes
...
[2019-06-15T23:53:34.223Z] INFO: salty-dog/19839 on cerberus: all rules passed
> cat examples/kubernetes-resources-fail.yml | salty-dog \
--rules rules/kubernetes.yml \
--tag kubernetes
...
[2019-06-15T23:56:04.764Z] ERROR: salty-dog/22211 on cerberus: some rules failed (errors=1)
The --source
and --dest
default to stdin and stdout, respectively, but a path may be provided:
> salty-dog \
--rules rules/kubernetes.yml \
--tag kubernetes \
--source examples/kubernetes-resources-pass.yml \
--dest /tmp/kubernetes-resource.yml
...
[2019-06-15T23:53:34.223Z] INFO: salty-dog/19839 on cerberus: all rules passed
Fix Mode
salty-dog
can also add default values to missing properties in fix
mode. If a rule does not immediately pass
with the --source
document, but defaults are provided in the schema, the defaults will be inserted before printing to
--dest
.
> salty-dog fix \
--source examples/kubernetes-resources-some.yml \
--rules rules/kubernetes.yml \
--tag kubernetes
Default Values
Properties that appear in the schema with a default
provided will be added to each element as it is checked. Rules
apply in order, as do their defaults.
Coercing Values
Properties that appear in the document with a different type
than they have in the schema may be coerced, if the
value is compatible with the schema type. The full matrix of valid type coercions
is documented by Ajv.
List Mode
salty-dog
can list the active set of rules, to help debug tags and inclusion. Both --source
and --dest
are
ignored in list
mode.
> salty-dog list \
--rules rules/kubernetes.yml \
--tag kubernetes
...
[2019-06-30T18:39:11.930Z] INFO: salty-dog/26330 on cerberus: listing active rules
rules: [
{
"desc": "resource limits are too low",
"level": "debug",
"name": "kubernetes-resources-minimum-cpu",
...
]
Rules
Rules combine a jsonpath expression and JSON schema to select and validate the document.
The rule's select
expression is used to select nodes that should be validated, which are filter
ed, then check
ed.
The structure of rule files and the rules within them are documented here.
Enable Rules
All rules are disabled by default and must be enabled by name, level, or tag.
To enable a single rule by name, --include-name foo-rule
.
To enable a group of rules by level, --include-level warn
.
To enable a group of rules by tag, --include-tag foo
.
Load Rules
Rules can be loaded from a file, module, or path.
To load a file by name, --rule-file foo.yml
. This will accept any extension.
To load a module, --rule-module foo
. The required module exports are documented here.
To load a path, --rule-path foo/
. This will recursively load any files matching *.+(json|yaml|yml)
.
Validate Rules
To validate the rules in the rules/
directory using the meta-rules:
> make test-rules
...
{"name":"salty-dog","hostname":"cerberus","pid":29403,"level":30,"msg":"all rules passed","time":"2019-06-16T00:56:55.132Z","v":0}