SimonBaeumer/goss

View on GitHub
docs/manual.md

Summary

Maintainability
Test Coverage
# goss manual

## Table of Contents

* [Table of Contents](#table-of-contents)
* [Usage](#usage)
  * [global options](#global-options)
    * [\-g gossfile](#-g-gossfile)
  * [commands](#commands)
    * [add, a \- Add system resource to test suite](#add-a---add-system-resource-to-test-suite)
    * [autoadd, aa \- Auto add all matching resources to test suite](#autoadd-aa---auto-add-all-matching-resources-to-test-suite)
    * [render, r \- Render gossfile after importing all referenced gossfiles](#render-r---render-gossfile-after-importing-all-referenced-gossfiles)
    * [serve, s \- Serve a health endpoint](#serve-s---serve-a-health-endpoint)
    * [validate, v \- Validate the system](#validate-v---validate-the-system)
* [Important note about goss file format](#important-note-about-goss-file-format)
* [Available tests](#available-tests)
  * [addr](#addr)
  * [command](#command)
  * [dns](#dns)
  * [file](#file)
  * [gossfile](#gossfile)
  * [group](#group)
  * [http](#http)
  * [interface](#interface)
  * [kernel-param](#kernel-param)
  * [mount](#mount)
  * [matching](#matching)
  * [package](#package)
  * [port](#port)
  * [process](#process)
  * [service](#service)
  * [user](#user)
* [Patterns](#patterns)
* [Advanced Matchers](#advanced-matchers)
* [Templates](#templates)

## Usage

```
NAME:
   goss - Quick and Easy server validation

USAGE:
   goss [global options] command [command options] [arguments...]

VERSION:
   0.0.0

COMMANDS:
   validate, v    Validate system
   serve, s    Serve a health endpoint
   render, r    render gossfile after imports
   autoadd, aa    automatically add all matching resource to the test suite
   add, a    add a resource to the test suite
   help, h    Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --gossfile, -g "./goss.yaml"    Goss file to read from / write to [$GOSS_FILE]
   --vars value                 json/yaml file containing variables for template [$GOSS_VARS]
   --package             Package type to use [rpm, deb, apk, pacman]
   --help, -h            show help
   --generate-bash-completion
   --version, -v        print the version

```
**Note:** *Most flags can be set by using environment variables, see `--help` for more info.*


## global options
### -g gossfile
The file to use when reading/writing tests. Use `-g -` to read from `STDIN`.

Valid formats:
* **YAML** (default)
* **JSON**

### --vars
The file to read variables from when rendering gossfile [templates](#templates).

Valid formats:
* **YAML** (default)
* **JSON**

### --package <type>
The package type to check for.

Valid options are:
* `apk`
* `deb`
* `pacman`
* `rpm`


## commands
Commands are the actions goss can run.

* [add](#add-a---add-system-resource-to-test-suite): add a single test for a resource
* [autoadd](#autoadd-aa---auto-add-all-matching-resources-to-test-suite): automatically add multiple tests for a resource
* [render](#render-r---render-gossfile-after-importing-all-referenced-gossfiles): renders and outputs the gossfile, importing all included gossfiles
* [serve](#serve-s---serve-a-health-endpoint): serves the gossfile validation as an HTTP endpoint on a specified address and port, so you can use your gossfile as a health repor for the host
* [validate](#validate-v---validate-the-system): runs the goss test suite on your server


### add, a - Add system resource to test suite

This will add a test for a resource. Non existent resources will add a test to ensure they do not exist on the system. A sub-command *resource type* has to be provided when running `add`.

#### Resource types
* `addr` - can verify if a remote `address:port` is reachable, see [addr](#addr)
* `command` - can run a [command](#command) and validate the exit status and/or output
* `dns` - resolves a [dns](#dns) name and validates the addresses
* `file` - can validate a [file](#file) existence, permissions, stats (size, etc) and contents
* `goss` - allows you to include the contents of another [gossfile](#gossfile)
* `group` - can validate the existence and values of a [group](#group) on the system
* `http` - can validate the HTTP response code and content of a URI, see [http](#http)
* `interface` - can validate the existence and values (es. the addresses) of a network interface, see [interface](#interface)
* `kernel-param` - can validate kernel parameters (sysctl values), see [kernel-param](#kernel-param)
* `mount` - can validate the existence and options relative to a [mount](#mount) point
* `package` - can validate the status of a [package](#package) using the package manager specified on the commandline with `--package`
* `port` - can validate the status of a local [port](#port), for example `80` or `udp:123`
* `process` - can validate the status of a [process](#process)
* `service` - can validate if a [service](#service) is running and/or enabled at boot
* `user` - can validate the existence and values of a [user](#user) on the system

#### Flags
##### --exclude-attr
Ignore **non-required** attribute(s) matching the provided glob when adding a new resource, may be specified multiple times.

#### Example:
```bash
$ goss a file /etc/passwd
$ goss a user nobody
$ goss a --exclude-attr home --exclude-attr shell user nobody
$ goss a --exclude-attr '*' user nobody
```


### autoadd, aa - Auto add all matching resources to test suite
Automatically [adds](#add-a---add-system-resource-to-test-suite) all **existing** resources matching the provided argument.

Will automatically add the following matching resources:
* `file` - only if argument contains `/`
* `group`
* `package`
* `port`
* `process` - Also adding any ports it's listening to (if run as root)
* `service`
* `user`

Will **NOT** automatically add:
* `addr`
* `command` - for safety
* `dns`
* `http`
* `interface`
* `kernel-param`
* `mount`

#### Example:
```bash
$ goss autoadd sshd
```

Generates the following `goss.yaml`
```yaml
port:
  tcp:22:
    listening: true
    ip:
    - 0.0.0.0
  tcp6:22:
    listening: true
    ip:
    - '::'
service:
  sshd:
    enabled: true
    running: true
user:
  sshd:
    exists: true
    uid: 74
    gid: 74
    groups:
    - sshd
    home: /var/empty/sshd
    shell: /sbin/nologin
group:
  sshd:
    exists: true
    gid: 74
process:
  sshd:
    running: true
```


### render, r - Render gossfile after importing all referenced gossfiles
This command allows you to keep your tests separated and render a single, valid, gossfile, by including them with the `gossfile` directive.

#### Flags
##### --debug
This prints the rendered golang template prior to printing the parsed JSON/YAML gossfile.

#### Example:
```bash

$ cat goss_httpd_package.yaml
package:
  httpd:
    installed: true
    versions:
    - 2.2.15

$ cat goss_httpd_service.yaml
service:
  httpd:
    enabled: true
    running: true

$ cat goss_nginx_service-NO.yaml
service:
  nginx:
    enabled: false
    running: false

$ cat goss.yaml
gossfile:
  goss_httpd_package.yaml: {}
  goss_httpd_service.yaml: {}
  goss_nginx_service-NO.yaml: {}

$ goss -g goss.yaml render
package:
  httpd:
    installed: true
    versions:
    - 2.2.15
service:
  httpd:
    enabled: true
    running: true
  nginx:
    enabled: false
    running: false
```


### serve, s - Serve a health endpoint

`serve` exposes the goss test suite as a health endpoint on your server. The end-point will return the stest results in the format requested and an http status of 200 or 503.

`serve` will look for a test suite in the same order as [validate](#validate-v---validate-the-system)

#### Flags
* `--cache <value>`, `-c <value>` - Time to cache the results (default: 5s)
* `--endpoint <value>`, `-e <value>` - Endpoint to expose (default: `/healthz`)
* `--format`, `-f` - output format, same as [validate](#validate-v---validate-the-system)
* `--listen-addr [ip]:port`, `-l [ip]:port` - Address to listen on (default: `:8080`)
* `--max-concurrent` - Max number of tests to run concurrently

#### Example:

```bash
$ goss serve &
$ curl http://localhost:8080/healthz

# JSON endpoint
$ goss serve --format json &
$ curl localhost:8080/healthz
```


### validate, v - Validate the system

`validate` runs the goss test suite on your server. Prints an rspec-like (by default) output of test results. Exits with status 0 on success, non-0 otherwise.

#### Flags
* `--format`, `-f` (output format)
  * `documentation` - Verbose test results
  * `json` - Detailed test result
  * `json_oneline` - Same as json, but oneliner
  * `junit`
  * `nagios` - Nagios/Sensu compatible output /w exit code 2 for failures.
  * `rspecish` **(default)** - Similar to rspec output
  * `tap`
  * `silent` - No output. Avoids exposing system information (e.g. when serving tests as a healthcheck endpoint).
* `--format-options`, `-o` (output format option)
  * `perfdata` - Outputs Nagios "performance data". Applies to `nagios` output.
  * `verbose` - Gives verbose output. Applies to `nagios` output.
* `--max-concurrent` - Max number of tests to run concurrently
* `--no-color` - Disable color
* `--color` - Force enable color
* `--retry-timeout`, `-r` - Retry on failure so long as elapsed + sleep time is less than this (default: 0)
* `--sleep`, `-s` - Time to sleep between retries (default: 1s)

#### Example:

```bash
$ goss validate --format documentation
File: /etc/hosts: exists: matches expectation: [true]
DNS: localhost: resolvable: matches expectation: [true]
[...]
Total Duration: 0.002s
Count: 10, Failed: 2, Skipped: 0

$ curl -s https://static/or/dynamic/goss.json | goss validate
...F.F
[...]
Total Duration: 0.002s
Count: 6, Failed: 2, Skipped: 0

$ goss render | ssh remote-host 'goss -g - validate'
......

Total Duration: 0.002s
Count: 6, Failed: 0, Skipped: 0

$ goss validate --format nagios -o verbose  -o perfdata
GOSS CRITICAL - Count: 76, Failed: 1, Skipped: 0, Duration: 1.009s|total=76 failed=1 skipped=0 duration=1.009s
Fail 1 - DNS: localhost: addrs: doesn't match, expect: [["127.0.0.1","::1"]] found: [["127.0.0.1"]]
$ echo $?
2
```

## Important note about goss file format
It is important to note that both YAML and JSON are formats that describe a nested data structure.

**WRONG way to write a goss file**

```yaml
file:
  /etc/httpd/conf/httpd.conf:
    exists: true

service:
  httpd:
    enabled: true
    running: true

file:
  /var/www/html:
    filetype: directory
    exists: true  
```

If you try to validate this file, it will **only** run the second `file` test:

```bash
# goss validate --format documentation
File: /var/www/html: exists: matches expectation: [true]
File: /var/www/html: filetype: matches expectation: ["directory"]
Service: httpd: enabled: matches expectation: [true]
Service: httpd: running: matches expectation: [true]

Total Duration: 0.014s
Count: 8, Failed: 0, Skipped: 0
```

As you can see, the first `file` check has not been run because the second `file` entry *overwrites* the previous one.

You need to make sure all the entries of the same type are under the same declaration.

**This is the CORRECT way to write a goss file**

```yaml
file:
  /etc/httpd/conf/httpd.conf:
    exists: true
  /var/www/html:
    filetype: directory
    exists: true  

service:
  httpd:
    enabled: true
    running: true
```

Running validate with this configuration will correctly check both files:

```bash
# goss validate --format documentation
File: /var/www/html: exists: matches expectation: [true]
File: /var/www/html: filetype: matches expectation: ["directory"]
File: /etc/httpd/conf/httpd.conf: exists: matches expectation: [true]
Service: httpd: enabled: matches expectation: [true]
Service: httpd: running: matches expectation: [true]

Total Duration: 0.014s
Count: 10, Failed: 0, Skipped: 0
```

Please note that using the `goss add` and `goss autoadd` command will create a valid file, but if you're writing your files by hand you'll save a lot of time by taking this in consideration.

If you want to keep your tests in separate files, the best way to obtain a single, valid, file is to create a main goss file that includes the others with the [gossfile](#gossfile) directive and then [render](#render-r---render-gossfile-after-importing-all-referenced-gossfiles) it.



## Available tests

* [addr](#addr)
* [command](#command)
* [dns](#dns)
* [file](#file)
* [gossfile](#gossfile)
* [group](#group)
* [http](#http)
* [interface](#interface)
* [kernel-param](#kernel-param)
* [matching](#matching)
* [mount](#mount)
* [package](#package)
* [port](#port)
* [process](#process)
* [service](#service)
* [user](#user)


### addr
Validates if a remote `address:port` are accessible.

```yaml
tcp://ip-address-or-domain-name:80:
  reachable: true
  timeout: 500
```


### command
Validates the exit-status and output of a command

```yaml
command:
  go version:
    # required attributes
    exit-status: 0
    # optional attributes
    stdout:
    - go version go1.6 linux/amd64
    stderr: []
    timeout: 10000 # in milliseconds
```

`stdout` and `stderr` can be a string or [pattern](#patterns)


### dns
Validates that the provided address is resolvable and the addrs it resolves to.

```yaml
dns:
  localhost:
    # required attributes
    resolvable: true
    # optional attributes
    server: 8.8.8.8
    addrs:
    - 127.0.0.1
    - ::1
    timeout: 500 # in milliseconds
```

With the server attribute set, it is possible to validate the following types of DNS record:

- A
- AAAA
- CAA
- CNAME
- MX
- NS
- PTR
- SRV
- TXT

To validate specific DNS address types, prepend the hostname with the type and a colon, a few examples:

```yaml
dns:
  # Validate a CNAME record
  CNAME:dnstest.github.io:
    resolvable: true
    server: 8.8.8.8
    addrs:
    - "github.map.fastly.net."

  # Validate a PTR record
  PTR:8.8.8.8:
    resolvable: true
    server: 8.8.8.8
    addrs:
    - "google-public-dns-a.google.com."

  # Validate and SRV record
  SRV:_https._tcp.dnstest.io:
    resolvable: true
    server: 8.8.8.8
    addrs:
    - "0 5 443 a.dnstest.io."
    - "10 10 443 b.dnstest.io."
```

Please note that if you want `localhost` to **only** resolve `127.0.0.1` you'll need to use [Advanced Matchers](#advanced-matchers)

```yaml
dns:
  localhost:
    resolvable: true
    addrs:
      consist-of: [127.0.0.1]
    timeout: 500 # in milliseconds
```

### file
Validates the state of a file, directory, or symbolic link

```yaml
file:
  /etc/passwd:
    # required attributes
    exists: true
    # optional attributes
    mode: "0644"
    size: 2118 # in bytes
    owner: root
    group: root
    filetype: file # file, symlink, directory
    contains: [] # Check file content for these patterns
    md5: 7c9bb14b3bf178e82c00c2a4398c93cd # md5 checksum of file
    # A stronger checksum alternative to md5 (recommended)
    sha256: 7f78ce27859049f725936f7b52c6e25d774012947d915e7b394402cfceb70c4c
  /etc/alternatives/mta:
    # required attributes
    exists: true
    # optional attributes
    filetype: symlink # file, symlink, directory
    linked-to: /usr/sbin/sendmail.sendmail
```

`contains` can be a string or a [pattern](#patterns)


### gossfile
Import other gossfiles from this one. This is the best way to maintain a large number of tests, and/or create profiles. See [render](#render-r---render-gossfile-after-importing-all-referenced-gossfiles) for more examples. Glob patterns can be also be used to specify matching gossfiles.

```yaml
gossfile:
  goss_httpd.yaml: {}
  /etc/goss.d/*.yaml: {}
```


### group
Validates the state of a group

```yaml
group:
  nfsnobody:
    # required attributes
    exists: true
    # optional attributes
    gid: 65534
```


### http
Validates HTTP response status code and content.

```yaml
http:
  https://www.google.com:
    # required attributes
    status: 200
    # optional attributes
    allow-insecure: false
    no-follow-redirects: false # Setting this to true will NOT follow redirects
    timeout: 1000
    body: [] # Check http response content for these patterns
    username: "" # username for basic auth
    password: "" # password for basic auth
    cert: /home/example/client.crt # Client certificate file which can be used for cert authentication
    key: /home/example/client.key # Client private key file
    headers: # Check for http headers
      key:
        - value
        - another value
    request-headers: # define headers should be send within the request
      key:
        - value
```


### interface
Validates network interface values

```yaml
interface:
  eth0:
    # required attributes
    exists: true
    # optional attributes
    addrs:
    - 172.17.0.2/16
    - fe80::42:acff:fe11:2/64
    mtu: 1500
```


### kernel-param
Validates kernel param (sysctl) value.

```yaml
kernel-param:
  kernel.ostype:
    # required attributes
    value: Linux
```

To see the full list of current values, run `sysctl -a`.


### mount
Validates mount point attributes.

```yaml
mount:
  /home:
    # required attributes
    exists: true
    # optional attributes
    opts:
    - rw
    - relatime
    source: /dev/mapper/fedora-home
    filesystem: xfs
```

### matching
Validates specified content against a matcher. Best used with [Templates](#templates).

#### With [Templates](#templates):
Let's say we have a `data.json` file that gets generated as part of some testing pipeline:

```json
{
  "instance_count": 14,
  "failures": 3,
  "status": "FAIL"
}
```

This could then be passed into goss: `goss --vars data.json validate`

And then validated against:

```yaml
matching:
  check_instance_count: # Make sure there is at least one instance
    content: {{ .Vars.instance_count }}
    matches:
      gt: 0

  check_failure_count_from_all_instance: # expect no failures
    content: {{ .Vars.failures }}
    matches: 0

  check_status:
    content: {{ .Vars.status }}
    matches:
      not: FAIL
```

#### Without [Templates](#templates):
```yaml
matching:
  has_substr: # friendly test name
    content: some string
    matches:
      match-regexp: some str
  has_2:
    content:
      - 2
    matches:
      contain-element: 2
  has_foo_bar_and_baz:
    content:
      foo: bar
      baz: bing
    matches:
      and:
        - have-key-with-value:
            foo: bar
        - have-key: baz
```

### package
Validates the state of a package

```yaml
package:
  httpd:
    # required attributes
    installed: true
    # optional attributes
    package: pacman #set a specific package manager
    versions:
    - 2.2.15
```


### port
Validates the state of a local port.

**Note:** Goss might consider your port to be listening on `tcp6` rather than `tcp`, try running `goss add port ..` to see how goss detects it. ([explanation](https://github.com/aelsabbahy/goss/issues/149))

```yaml
port:
  # {tcp,tcp6,udp,udp6}:port_num
  tcp:22:
    # required attributes
    listening: true
    # optional attributes
    ip: # what IP(s) is it listening on
    - 0.0.0.0
```


### process
Validates if a process is running.

```yaml
process:
  chrome:
    # required attributes
    running: true
```


### service
Validates the state of a service.

```yaml
service:
  sshd:
    # required attributes
    enabled: true
    running: true
```

**NOTE:** this will **not** automatically check if the process is alive, it will check the status from `systemd`/`upstart`/`init`.


### user
Validates the state of a user

```yaml
user:
  nfsnobody:
    # required attributes
    exists: true
    # optional attributes
    uid: 65534
    gid: 65534
    groups:
    - nfsnobody
    home: /var/lib/nfs
    shell: /sbin/nologin
```



## Patterns
For the attributes that use patterns (ex. `file`, `command` `output`), each pattern is checked against the attribute string, the type of patterns are:

* `"string"` - checks if any line contain string.
* `"!string"` - inverse of above, checks that no line contains string
* `"\\!string"` - escape sequence, check if any line contains `"!string"`
* `"/regex/"` - verifies that line contains regex
* `"!/regex/"` - inverse of above, checks that no line contains regex

**NOTE:** Pattern attributes do not support [Advanced Matchers](#advanced-matchers)

**NOTE:** Regex support is based on golang's regex engine documented [here](https://golang.org/pkg/regexp/syntax/)

**NOTE:** You will **need** the double backslash (`\\`) escape for Regex special entities, for example `\\s` for blank spaces.

### Example
```bash
$ cat /tmp/test.txt
found
!alsofound


$ cat goss.yaml
file:
  /tmp/test.txt:
    exists: true
    contains:
    - found
    - /fou.d/
    - "\\!alsofound"
    - "!missing"
    - "!/mis.ing/"

$ goss validate
..

Total Duration: 0.001s
Count: 2, Failed: 0
```

## Advanced Matchers
Goss supports advanced matchers by converting json input to [gomega](https://onsi.github.io/gomega/) matchers.

### Examples

Validate that user `nobody` has a `uid` that is less than `500` and that they are **only** a member of the `nobody` group.

```yaml
user:
  nobody:
    exists: true
    uid:
      lt: 500
    groups:
      consist-of: [nobody]
```

Matchers can be nested for more complex logic, for example you can ensure that you have 3 kernel versions installed and none of them are `4.1.0`:

```yaml
package:
  kernel:
    installed: true
    versions:
      and:
        - have-len: 3
        - not:
            contain-element: "4.1.0"
```

For more information see:
* [gomega_test.go](https://github.com/aelsabbahy/goss/blob/master/resource/gomega_test.go) - For a complete set of supported json -> Gomega mapping
* [gomega](https://onsi.github.io/gomega/) - Gomega matchers reference

## Templates

Goss test files can leverage golang's [text/template](https://golang.org/pkg/text/template/) to allow for dynamic or conditional tests.

Available variables:
* `{{.Env}}`  - Containing environment variables
* `{{.Vars}}` - Containing the values defined in [--vars](#global-options) file

Available functions beyond text/template [built-in functions](https://golang.org/pkg/text/template/#hdr-Functions):
* `mkSlice "ARG1" "ARG2"` - Retuns a slice of all the arguments. See examples below for usage.
* `getEnv "var" ["default"]` - A more forgiving env var lookup. If key is missing either "" or default (if provided) is returned.
* `readFile "fileName"` - Reads file content into a string, trims whitespace. Useful when a file contains a token.
  * **NOTE:** Goss will error out during during the parsing phase if the file does not exist, no tests will be executed.
* `regexMatch "(some)?reg[eE]xp"` - Tests the piped input against the regular expression argument.

**NOTE:** gossfiles containing text/template `{{}}` controls will no longer work with `goss add/autoadd`. One way to get around this is to split your template and static goss files and use [gossfile](#gossfile) to import.

### Examples

Using [puppetlabs/facter](https://github.com/puppetlabs/facter) or [chef/ohai](https://github.com/chef/ohai) as external tools to provide vars.
```bash
$ goss --vars <(ohai) validate
$ goss --vars <(facter -j) validate
```

Using `mkSlice` to define a loop locally.
```yaml
file:
{{- range mkSlice "/etc/passwd" "/etc/group"}}
  {{.}}:
    exists: true
    mode: "0644"
    owner: root
    group: root
    filetype: file
{{end}}
```

Using Env variables and a vars file:

**vars.yaml:**
```yaml
centos:
  packages:
    kernel:
      - "4.9.11-centos"
      - "4.9.11-centos2"
debian:
  packages:
    kernel:
      - "4.9.11-debian"
      - "4.9.11-debian2"
users:
  - user1
  - user2
```

**goss.yaml:**
```yaml
package:
# Looping over a variables defined in a vars.yaml using $OS environment variable as a lookup key
{{range $name, $vers := index .Vars .Env.OS "packages"}}
  {{$name}}:
    installed: true
    versions:
    {{range $vers}}
      - {{.}}
    {{end}}
{{end}}

# This test is only when the OS environment variable matches the pattern
{{if .Env.OS | regexMatch "[Cc]ent(OS|os)"}}
  libselinux:
    installed: true
{{end}}

# Loop over users
user:
{{range .Vars.users}}
  {{.}}:
    exists: true
    groups:
    - {{.}}
    home: /home/{{.}}
    shell: /bin/bash
{{end}}


package:
{{if eq .Env.OS "centos"}}
  # This test is only when $OS environment variable is set to "centos"
  libselinux:
    installed: true
{{end}}
```

Rendered results:
```bash
# To validate:
$ OS=centos goss --vars vars.yaml validate
# To render:
$ OS=centos goss --vars vars.yaml render
# To render with debugging enabled:
$ OS=centos goss --vars vars.yaml render --debug
```