Skip to content

Commit

Permalink
Dgoss (#206)
Browse files Browse the repository at this point in the history
Add dgoss wrapper for goss.
Add getEnv and readFile functions to templates
Add --color flag to force color mode
  • Loading branch information
aelsabbahy authored Mar 15, 2017
1 parent 2cfa77b commit 954e8ed
Show file tree
Hide file tree
Showing 9 changed files with 211 additions and 35 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ deploy:
file:
- release/goss-linux-amd64
- release/goss-linux-386
- extras/dgoss/dgoss
skip_cleanup: true
on:
repo: aelsabbahy/goss
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

**Note:** For an even faster way of doing this, see: [autoadd](https://github.com/aelsabbahy/goss/blob/master/docs/manual.md#autoadd-aa---auto-add-all-matching-resources-to-test-suite)

**Note:** For some Docker use cases, see my blog post [here](https://medium.com/@aelsabbahy/docker-1-12-kubernetes-simplified-health-checks-and-container-ordering-with-goss-fa8debbe676c) and [dockerhub repo](https://hub.docker.com/r/aelsabbahy/goss/)
**Note:** For testing docker containers see the [dgoss](https://github.com/aelsabbahy/goss/tree/master/extras/dgoss) wrapper

**Note:** For some Docker/Kubernetes healtcheck, health endpoint, and container ordering examples, see my blog post [here](https://medium.com/@aelsabbahy/docker-1-12-kubernetes-simplified-health-checks-and-container-ordering-with-goss-fa8debbe676c)

<a href="https://asciinema.org/a/4suhr8p42qcn6r7crfzt6cc3e?autoplay=1" target="_blank"><img src="https://cloud.githubusercontent.com/assets/6783261/17330426/ce7ad066-5894-11e6-84ea-29fd4207af58.gif" alt="asciicast"></a>

Expand All @@ -27,6 +29,7 @@ Goss is a YAML based [serverspec](http://serverspec.org/) alternative tool for v

## Installation

This will install goss and [dgoss](https://github.com/aelsabbahy/goss/tree/master/extras/dgoss) by default.
**Note:** Using `curl | sh` is not recommended for production systems, use manual installation below.

```bash
Expand All @@ -40,8 +43,12 @@ curl -fsSL https://goss.rocks/install | GOSS_VER=v0.3.0 GOSS_DST=~/bin sh
### Manual installation
```bash
# See https://github.com/aelsabbahy/goss/releases for release versions
curl -L https://github.com/aelsabbahy/goss/releases/download/v0.3.0/goss-linux-amd64 -o /usr/local/bin/goss
curl -L https://github.com/aelsabbahy/goss/releases/download/_VERSION_/goss-linux-amd64 -o /usr/local/bin/goss
chmod +rx /usr/local/bin/goss

# (optional) dgoss docker wrapper (use 'master' for latest version)
curl -L https://github.com/aelsabbahy/goss/tree/_VERSION_/extras/dgoss/dgoss -o /usr/local/bin/dgoss
chmod +rx /usr/local/bin/dgoss
```

## Full Documentation
Expand Down
5 changes: 5 additions & 0 deletions cmd/goss/goss.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ func main() {
Usage: fmt.Sprintf("Format to output in, valid options: %s", outputs.Outputers()),
EnvVar: "GOSS_FMT",
},
cli.BoolFlag{
Name: "color",
Usage: "Force color on",
EnvVar: "GOSS_COLOR",
},
cli.BoolFlag{
Name: "no-color",
Usage: "Force color off",
Expand Down
6 changes: 5 additions & 1 deletion docs/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ $ curl localhost:8080/healthz
* `TAP`
* `--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)

Expand Down Expand Up @@ -761,7 +762,10 @@ Available 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` - Retuns a slice of all the arguments. See examples below for usage.
* `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 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.

**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.

Expand Down
53 changes: 53 additions & 0 deletions extras/dgoss/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# dgoss

dgoss is a convenience wrapper around goss that aims to bring the simplicity of goss to docker containers.


## Usage

`dgoss [run|edit] <docker_run_params>`

### Run

Run is used to validate a docker container. It expects a `./goss.yaml` file to exist in the directory it was invoked from. In most cases one can just substitute the docker command for the dgoss command, for example:

**run:**

`docker run -e JENKINS_OPTS="--httpPort=8080 --httpsPort=-1" -e JAVA_OPTS="-Xmx1048m" jenkins:alpine`

**test:**

`dgoss run -e JENKINS_OPTS="--httpPort=8080 --httpsPort=-1" -e JAVA_OPTS="-Xmx1048m" jenkins:alpine`


`dgoss run` will do the following:
* Run the container with the flags you specified.
* Stream the containers log output into the container as `/goss/docker_output.log`
* This allows writing tests or waits against the docker output
* (optional) Run `goss` with `$GOSS_WAIT_OPTS` if `./goss_wait.yaml` file exists in the current dir
* Run `goss` with `$GOSS_OPTS` using `./goss.yaml`


### Edit

Edit will launch a docker container, install goss, and drop the user into an interactive shell. Once the user quits the interactive shell, any `goss.yaml` or `goss_wait.yaml` are copied out into the current directory. This allows the user to leverage the `goss add|autoadd` commands to write tests as they would on a regular machine.

**Example:**

`dgoss edit -e JENKINS_OPTS="--httpPort=8080 --httpsPort=-1" -e JAVA_OPTS="-Xmx1048m" jenkins:alpine`

### Environment vars and defaults
The following environment variables can be set to change the behavior of dgoss.

##### GOSS_PATH
Location of the goss binary to use. (Default: `$(which goss)`)

##### GOSS_OPTS
Options to use for the goss test run. (Default: `--color --format documentation`)

##### GOSS_WAIT_OPTS
Options to use for the goss wait run, when `./goss_wait.yaml` exists. (Default: `-r 30s -s 1s > /dev/null`)

##### GOSS_SLEEP
Time to sleep after running container (and optionally `goss_wait.yaml`) and before running tests. (Default: `0.2`)

70 changes: 70 additions & 0 deletions extras/dgoss/dgoss
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/bin/bash

set -e

USAGE="$(basename "$0") [run|edit] <docker_run_params>"

info() { echo -e "INFO: $*"; }
error() { echo -e "ERROR: $*";exit 1; }

cleanup() {
set +e
{ kill "$log_pid" && wait "$log_pid"; } 2> /dev/null
rm -rf "$tmp_dir"
if [[ $id ]];then
info "Deleting container"
docker rm -vf "$id" > /dev/null
fi
}

run(){
# Copy in goss
cp "${GOSS_PATH}" "$tmp_dir/goss"
chmod 755 "$tmp_dir/goss"
[[ -e goss.yaml ]] && cp goss.yaml "$tmp_dir"
[[ -e goss_wait.yaml ]] && cp goss_wait.yaml "$tmp_dir"
info "Starting docker container"
id=$(docker run -d -v "$tmp_dir:/goss" "${@:2}")
docker logs -f "$id" > "$tmp_dir/docker_output.log" 2>&1 &
log_pid=$!
info "Container ID: ${id:0:8}"
}

get_docker_file() {
if docker exec "$id" sh -c "test -e $1" > /dev/null;then
docker cp "$id:$1" .
fi
}

# Main
tmp_dir=$(mktemp -d)
chmod 777 "$tmp_dir"
trap 'ret=$?;cleanup;exit $ret' EXIT

GOSS_PATH="${GOSS_PATH:-$(which goss)}"
[[ $GOSS_PATH ]] || { error "Couldn't find goss installation, please set GOSS_PATH to it"; }
[[ ${GOSS_OPTS+x} ]] || GOSS_OPTS="--color --format documentation"
[[ ${GOSS_WAIT_OPTS+x} ]] || GOSS_WAIT_OPTS="-r 30s -s 1s > /dev/null"
GOSS_SLEEP=${GOSS_SLEEP:-0.2}

case "$1" in
run)
run "$@"
if [[ -e goss_wait.yaml ]]; then
info "Found goss_wait.yaml, waiting for it to pass before running tests"
docker exec "$id" sh -c "/goss/goss -g /goss/goss_wait.yaml validate $GOSS_WAIT_OPTS"
fi
[[ $GOSS_SLEEP ]] && { info "Sleeping for $GOSS_SLEEP"; sleep "$GOSS_SLEEP"; }
info "Running Tests"
docker exec "$id" sh -c "/goss/goss -g /goss/goss.yaml validate $GOSS_OPTS"
;;
edit)
run "$@"
info "Run goss add/autoadd to add resources"
docker exec -it "$id" sh -c 'cd /goss; PATH="/goss:$PATH" exec sh'
get_docker_file "/goss/goss.yaml"
get_docker_file "/goss/goss_wait.yaml"
;;
*)
error "$USAGE"
esac
32 changes: 0 additions & 32 deletions store.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package goss

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
Expand All @@ -11,7 +10,6 @@ import (
"reflect"
"sort"
"strings"
"text/template"

"gopkg.in/yaml.v2"

Expand Down Expand Up @@ -94,36 +92,6 @@ func varsFromFile(varsFile string) (map[string]interface{}, error) {
return vars, nil
}

func mkSlice(args ...interface{}) []interface{} {
return args
}

func NewTemplateFilter(varsFile string) func([]byte) []byte {
vars, err := varsFromFile(varsFile)
if err != nil {
fmt.Printf("Error: loading vars file '%s'\n%v\n", varsFile, err)
os.Exit(1)
}
tVars := &TmplVars{Vars: vars}

f := func(data []byte) []byte {
funcMap := map[string]interface{}{"mkSlice": mkSlice}
t := template.New("test").Funcs(template.FuncMap(funcMap))
tmpl, err := t.Parse(string(data))
if err != nil {
log.Fatal(err)
}
tmpl.Option("missingkey=error")
var doc bytes.Buffer
err = tmpl.Execute(&doc, tVars)
if err != nil {
log.Fatal(err)
}
return doc.Bytes()
}
return f
}

// Reads json byte array returning GossConfig
func ReadJSONData(data []byte, detectFormat bool) GossConfig {
if TemplateFilter != nil {
Expand Down
65 changes: 65 additions & 0 deletions template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package goss

import (
"bytes"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"text/template"
)

func mkSlice(args ...interface{}) []interface{} {
return args
}

func readFile(f string) (string, error) {
b, err := ioutil.ReadFile(f)
if err != nil {
return "", err

}
return strings.TrimSpace(string(b)), nil
}

func getEnv(key string, def ...string) string {
val := os.Getenv(key)
if val == "" && len(def) > 0 {
return def[0]
}

return os.Getenv(key)
}

var funcMap = map[string]interface{}{
"mkSlice": mkSlice,
"readFile": readFile,
"getEnv": getEnv,
}

func NewTemplateFilter(varsFile string) func([]byte) []byte {
vars, err := varsFromFile(varsFile)
if err != nil {
fmt.Printf("Error: loading vars file '%s'\n%v\n", varsFile, err)
os.Exit(1)
}
tVars := &TmplVars{Vars: vars}

f := func(data []byte) []byte {
funcMap := funcMap
t := template.New("test").Funcs(template.FuncMap(funcMap))
tmpl, err := t.Parse(string(data))
if err != nil {
log.Fatal(err)
}
tmpl.Option("missingkey=error")
var doc bytes.Buffer
err = tmpl.Execute(&doc, tVars)
if err != nil {
log.Fatal(err)
}
return doc.Bytes()
}
return f
}
3 changes: 3 additions & 0 deletions validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ func getOutputer(c *cli.Context) outputs.Outputer {
if c.Bool("no-color") {
color.NoColor = true
}
if c.Bool("color") {
color.NoColor = false
}
return outputs.GetOutputer(c.String("format"))
}

Expand Down

0 comments on commit 954e8ed

Please sign in to comment.