Permalink
Browse files

Dgoss (#206)

Add dgoss wrapper for goss.
Add getEnv and readFile functions to templates
Add --color flag to force color mode
  • Loading branch information...
aelsabbahy committed Mar 15, 2017
1 parent 2cfa77b commit 954e8ed009013bcb284c2bb125d63da840318455
Showing with 211 additions and 35 deletions.
  1. +1 −0 .travis.yml
  2. +9 −2 README.md
  3. +5 −0 cmd/goss/goss.go
  4. +5 −1 docs/manual.md
  5. +53 −0 extras/dgoss/README.md
  6. +70 −0 extras/dgoss/dgoss
  7. +0 −32 store.go
  8. +65 −0 template.go
  9. +3 −0 validate.go
View
@@ -27,6 +27,7 @@ deploy:
file:
- release/goss-linux-amd64
- release/goss-linux-386
- extras/dgoss/dgoss
skip_cleanup: true
on:
repo: aelsabbahy/goss
View
@@ -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>
@@ -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
@@ -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
View
@@ -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",
View
@@ -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)
@@ -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.
View
@@ -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`)
View
@@ -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
View
@@ -1,7 +1,6 @@
package goss
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
@@ -11,7 +10,6 @@ import (
"reflect"
"sort"
"strings"
"text/template"
"gopkg.in/yaml.v2"
@@ -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 {
View
@@ -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
}
View
@@ -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"))
}

0 comments on commit 954e8ed

Please sign in to comment.