2019-06-29 22:21:22 +00:00
|
|
|
# Salty Dog
|
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
`salty-dog` is a tool to validate JSON and YAML data using JSON schema rules. It can filter elements and validate
|
|
|
|
select parts of the document, supports multiple documents in the same stream or file, and can insert defaults during
|
|
|
|
validation.
|
|
|
|
|
|
|
|
## Getting Started
|
|
|
|
|
|
|
|
`salty-dog` is distributed as both a Docker container and an npm package, so it can be installed or pulled:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
# docker image
|
|
|
|
> docker pull ssube/salty-dog:master
|
|
|
|
|
|
|
|
# npm project install
|
|
|
|
> yarn add -D salty-dog
|
|
|
|
|
|
|
|
# npm global install
|
|
|
|
> 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 difficult to separate logs and the output document. Writing either the logs
|
|
|
|
or data to a file works around this.
|
|
|
|
|
|
|
|
To download, validate, and apply a Kubernetes resource:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
> 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
|
2021-08-13 02:27:09 +00:00
|
|
|
|
|
|
|
- [Salty Dog](#salty-dog)
|
2021-08-13 04:00:05 +00:00
|
|
|
- [Getting Started](#getting-started)
|
|
|
|
- [Contents](#contents)
|
|
|
|
- [Build](#build)
|
|
|
|
- [Local Build](#local-build)
|
|
|
|
- [Container Build](#container-build)
|
2021-08-13 02:27:09 +00:00
|
|
|
- [Usage](#usage)
|
2021-08-13 04:00:05 +00:00
|
|
|
- [Using The Container](#using-the-container)
|
|
|
|
- [Using The Package](#using-the-package)
|
|
|
|
- [Project Package](#project-package)
|
|
|
|
- [Global Package](#global-package)
|
|
|
|
- [Usage Modes](#usage-modes)
|
2021-08-13 02:27:09 +00:00
|
|
|
- [Rules](#rules)
|
2021-08-13 04:00:05 +00:00
|
|
|
- [Loading Rule Sources](#loading-rule-sources)
|
|
|
|
- [Using Rule Tags](#using-rule-tags)
|
|
|
|
- [Validating Rules](#validating-rules)
|
2021-08-13 02:27:09 +00:00
|
|
|
- [Output](#output)
|
2021-08-13 04:00:05 +00:00
|
|
|
- [Using Data Output](#using-data-output)
|
|
|
|
- [Inserting Default Values](#inserting-default-values)
|
|
|
|
- [Formatting Log Output](#formatting-log-output)
|
|
|
|
|
|
|
|
## Build
|
|
|
|
|
|
|
|
### Local Build
|
|
|
|
|
|
|
|
`salty-dog` is written in Typescript and requires `make`, `node`, and `yarn` to build.
|
|
|
|
|
|
|
|
```shell
|
|
|
|
> 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:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
> curl https://raw.githubusercontent.com/ssube/k8s-shards/master/roles/apps/gitlab/server/templates/ingress.yml | \
|
|
|
|
make run-stream \
|
|
|
|
1> >(kubectl apply --dry-run -f -) \
|
|
|
|
2> >($(yarn bin)/bunyan)
|
|
|
|
|
|
|
|
...
|
|
|
|
[2019-06-16T03:23:56.645Z] INFO: salty-dog/8015 on cerberus: all rules passed
|
|
|
|
ingress.extensions/gitlab created (dry run)
|
|
|
|
```
|
|
|
|
|
|
|
|
### Container Build
|
|
|
|
|
|
|
|
This method does not require the usual dependencies to be installed, only `docker` itself.
|
|
|
|
|
|
|
|
Build with Docker:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
# Stretch
|
|
|
|
docker run --rm -v "$(pwd):/salty-dog" -w /salty-dog node:16-stretch make
|
|
|
|
docker build -t salty-dog:stretch -f Dockerfile.stretch .
|
|
|
|
|
|
|
|
# Alpine
|
|
|
|
docker run --rm -v "$(pwd):/salty-dog" -w /salty-dog node:16-alpine sh -c "apk add build-base && make"
|
|
|
|
docker build -t salty-dog:alpine -f Dockerfile.alpine .
|
|
|
|
```
|
2019-06-29 22:21:22 +00:00
|
|
|
|
|
|
|
## Usage
|
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
`salty-dog` is distributed as a docker container and an npm package.
|
2019-06-29 22:21:22 +00:00
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
While the container is the preferred way of running `salty-dog`, it has one limitation: `docker run` combines
|
2019-06-29 22:21:22 +00:00
|
|
|
`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.
|
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
### Using The Container
|
|
|
|
|
|
|
|
To run the Docker container: `docker run --rm ssube/salty-dog:master`
|
|
|
|
|
|
|
|
The latest semi-stable image is published to `ssube/salty-dog:master`. Containers are published based on both Alpine
|
|
|
|
Linux and Debian (currently Stretch). All of the [available tags are listed here](https://hub.docker.com/r/ssube/salty-dog/tags).
|
|
|
|
|
|
|
|
Rules are provided in the image at `/salty-dog/rules`. To use custom rules in the container, mount them with
|
|
|
|
`-v $(pwd)/rules:/salty-dog/rules:ro` and load them with `--rules /rules/foo.yml`.
|
2021-08-13 02:27:09 +00:00
|
|
|
|
|
|
|
The `ssube/salty-dog` container image can be run normally or interactively.
|
2019-06-29 22:21:22 +00:00
|
|
|
|
2021-08-13 02:27:09 +00:00
|
|
|
To validate a file or input normally:
|
2019-06-29 22:21:22 +00:00
|
|
|
|
|
|
|
```shell
|
|
|
|
> docker run --rm ssube/salty-dog:master --help
|
|
|
|
```
|
|
|
|
|
2021-08-13 02:27:09 +00:00
|
|
|
You can also launch a shell within the container, using local rules:
|
2019-06-29 22:21:22 +00:00
|
|
|
|
|
|
|
```shell
|
2021-08-13 02:27:09 +00:00
|
|
|
> docker run \
|
|
|
|
--rm \
|
|
|
|
-it \
|
|
|
|
--entrypoint bash \
|
|
|
|
ssube/salty-dog:master
|
2019-06-29 22:21:22 +00:00
|
|
|
```
|
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
### Using The Package
|
2019-06-29 22:21:22 +00:00
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
`salty-dog` is also published as [an npm package](https://www.npmjs.com/package/salty-dog) with a binary, so it can
|
|
|
|
be used as a CLI command or programmatically.
|
2021-08-13 02:27:09 +00:00
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
#### Project Package
|
2021-08-13 02:27:09 +00:00
|
|
|
|
|
|
|
To install `salty-dog` for the current project:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
> yarn add -D salty-dog
|
|
|
|
> $(yarn bin)/salty-dog --help
|
|
|
|
```
|
2019-06-29 22:21:22 +00:00
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
#### Global Package
|
2019-06-29 22:21:22 +00:00
|
|
|
|
2021-08-13 02:27:09 +00:00
|
|
|
It is also possible to install `salty-dog` globally, rather than within a project. However, this is
|
|
|
|
not recommended.
|
|
|
|
|
2019-06-29 22:21:22 +00:00
|
|
|
```shell
|
|
|
|
> yarn global add salty-dog
|
|
|
|
> export PATH="${PATH}:$(yarn global bin)"
|
|
|
|
> salty-dog --help
|
|
|
|
```
|
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
### Usage Modes
|
|
|
|
|
|
|
|
TODO
|
|
|
|
|
2021-08-13 02:27:09 +00:00
|
|
|
## Rules
|
|
|
|
|
|
|
|
Rules are the core of `salty-dog` validation. Each rule has a JSON schema used to `check` the data, an optional
|
|
|
|
`filter` to skip some data, and a name and description.
|
|
|
|
|
|
|
|
For example:
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
name: salty-dog-gitlab-ci
|
|
|
|
rules:
|
|
|
|
- name: gitlab-stages
|
|
|
|
desc: should specify stages
|
|
|
|
level: info
|
|
|
|
tags:
|
|
|
|
- gitlab
|
|
|
|
- optional
|
|
|
|
check:
|
|
|
|
type: object
|
|
|
|
required: [stages]
|
|
|
|
properties:
|
|
|
|
stages:
|
|
|
|
type: array
|
|
|
|
items:
|
|
|
|
type: string
|
|
|
|
```
|
|
|
|
|
|
|
|
The complete rule format [is documented here](./rules.md).
|
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
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.
|
2021-08-13 02:27:09 +00:00
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
The structure of rule files and the rules within them [are documented here](docs/rules.md).
|
2021-08-13 02:27:09 +00:00
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
### Loading Rule Sources
|
2021-08-13 02:27:09 +00:00
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
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](./docs/rules.md#from-module).
|
|
|
|
|
|
|
|
To load a path, `--rule-path foo/`. This will recursively load any files matching `*.+(json|yaml|yml)`.
|
|
|
|
|
|
|
|
### Using Rule Tags
|
|
|
|
|
|
|
|
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`.
|
|
|
|
|
|
|
|
### Validating Rules
|
|
|
|
|
|
|
|
To validate the rules in the `rules/` directory using the meta-rules:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
> 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}
|
|
|
|
```
|
2021-08-13 02:27:09 +00:00
|
|
|
|
|
|
|
## Output
|
|
|
|
|
|
|
|
`salty-dog` outputs two streams: valid input data and error logs.
|
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
### Using Data Output
|
2021-08-13 02:27:09 +00:00
|
|
|
|
|
|
|
Valid input data is written back out to `stdout`, allowing `salty-dog` to be used inline with piped shell commands.
|
|
|
|
|
|
|
|
For example, to validate a kubernetes resource before applying it:
|
2019-06-29 22:21:22 +00:00
|
|
|
|
|
|
|
```shell
|
2021-08-13 02:27:09 +00:00
|
|
|
> 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)
|
|
|
|
```
|
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
#### Inserting Default Values
|
|
|
|
|
|
|
|
Properties that appear in the schema with the `default` key set will be added to each element as it is checked. Rules
|
|
|
|
apply in order, as do their defaults.
|
|
|
|
|
|
|
|
### Formatting Log Output
|
2021-08-13 02:27:09 +00:00
|
|
|
|
2021-08-13 04:00:05 +00:00
|
|
|
`salty-dog` uses [node-bunyan](https://github.com/trentm/node-bunyan) for logging and prints structured JSON output.
|
|
|
|
Logs can be pretty-printed by redirecting `stderr` through `bunyan` itself or `jq`, both of which are installed in
|
|
|
|
the `salty-dog` container.
|
2021-08-13 02:27:09 +00:00
|
|
|
|
|
|
|
To filter out error messages, then format the errors they contain:
|
|
|
|
|
|
|
|
```shell
|
|
|
|
> salty-dog | jq 'select(.level > 30) | {msg: .msg, errors: try (.errors[] | .msg), rule: try (.rule.desc)}'
|
|
|
|
|
|
|
|
[
|
|
|
|
"rule failed",
|
|
|
|
"containers must have complete resources specified"
|
|
|
|
]
|
|
|
|
[
|
|
|
|
"some rules failed",
|
|
|
|
"/resources/limits must have required property 'cpu' at item 0 of $.spec.template.spec.containers[*] for kubernetes-resources",
|
|
|
|
null
|
|
|
|
]
|
2019-06-29 22:21:22 +00:00
|
|
|
```
|