-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdgoss
executable file
·229 lines (207 loc) · 9.04 KB
/
dgoss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
#!/bin/bash
set -e
# shellcheck disable=SC2016
USAGE=$(printf "%s\n" \
"USAGE: $(basename "$0") [run|edit] <docker_run_params>" \
'' \
"ENVVARS:" \
' GOSS_PATH Location of `goss` executable (Default: tries `which goss`)' \
' GOSS_OPTS Options for `goss` (Default: `--color --format documentation`)' \
' GOSS_WAIT_OPTS Wait options for goss, used if goss files has a `goss_wait.yaml` file (Default: `-r 30s -s 1s > /dev/null`)' \
' GOSS_SLEEP Sleep delay before running `goss` (Default: `0.2`)' \
' GOSS_FILE Goss file to use (Default: `goss.yaml`)' \
' GOSS_VARS Optional goss vars file (Default: none)' \
' GOSS_FILES_PATH Directory to load goss files from (Default: `.`)' \
' CONTAINER_GOSS_PATH Directory inside container to copy goss files to (Default: `/goss`)' \
' GOSS_FILES_STRATEGY The strategy to get goss files into the container, ether `mount` or `cp` (Default: `mount`)' \
' DGOSS_TEMP_DIR Temporary directory for staging files for dgoss (Default: `/tmp.XXXXXXXXXX`)' \
' CONTAINER_MODE Mode to goss inside container, either `inject` or `entrypoint` (Default: `inject`)' \
' - Inject mode will start the container with your parameters and then execute `goss` inside it.' \
' This is the typical mode for daemon like containers (e.g. nginx).' \
' - Entrypoint mode is when you `goss` is the entrypoint.' \
' This mode always uses GOSS_FILES_STRATEGY=cp - it will force it' \
' The user has to give the docker run arguments to make `goss` the entrypoint.' \
' e.g. `--entrypoint /goss/goss`' \
' The user has to give `goss` arguments.' \
' Full example: `CONTAINER_MODE=entrypoint dgoss run -w /goss --entrypoint /goss/goss berney/s6 validate -f documentation --color`' \
' This mode is useful for tool like containers that are not long lived and will exit immediately.' \
' CONTAINER_RUNTIME Container runtime e.g. `docker`, `podman`, etc. (Default: `docker`)' \
' CONTAINER_LOG_OUTPUT Optional filepath to copy docker output logs to (Default: none)' \
''
)
info() {
echo -e "INFO: $*" >&2;
}
error() {
echo -e "ERROR: $*" >&2;
exit 1;
}
GOSS_FILES_PATH="${GOSS_FILES_PATH:-.}"
CONTAINER_GOSS_PATH="${CONTAINER_GOSS_PATH:-/goss}"
# Either `inject` or `entrypoint`
CONTAINER_MODE=${CONTAINER_MODE:-inject}
case "$CONTAINER_MODE" in
inject)
GOSS_FILES_STRATEGY=${GOSS_FILES_STRATEGY:="mount"}
;;
entrypoint)
info "Entrypoint mode forcing files strategy cp"
# shellcheck disable=SC2209
GOSS_FILES_STRATEGY=cp
;;
*)
# shellcheck disable=SC2016
error 'Wrong dgoss container mode, only `inject` or `entrypoint`'
;;
esac
# Container runtime
CONTAINER_RUNTIME="${CONTAINER_RUNTIME:-docker}"
cleanup() {
set +e
{ kill "$log_pid" && wait "$log_pid"; } 2> /dev/null
if [ -n "$CONTAINER_LOG_OUTPUT" ]; then
cp "$tmp_dir/docker_output.log" "$CONTAINER_LOG_OUTPUT"
fi
rm -rf "$tmp_dir"
if [[ $id ]];then
info "Deleting container"
$CONTAINER_RUNTIME rm -vf "$id" > /dev/null
fi
}
run(){
# Copy in goss
cp "${GOSS_PATH}" "$tmp_dir/goss"
chmod 755 "$tmp_dir/goss"
[[ -e "${GOSS_FILES_PATH}/${GOSS_FILE:-goss.yaml}" ]] && cp "${GOSS_FILES_PATH}/${GOSS_FILE:-goss.yaml}" "$tmp_dir/goss.yaml" && chmod 644 "$tmp_dir/goss.yaml"
[[ -e "${GOSS_FILES_PATH}/goss_wait.yaml" ]] && cp "${GOSS_FILES_PATH}/goss_wait.yaml" "$tmp_dir" && chmod 644 "$tmp_dir/goss_wait.yaml"
[[ -n "${GOSS_VARS}" ]] && [[ -e "${GOSS_FILES_PATH}/${GOSS_VARS}" ]] && cp "${GOSS_FILES_PATH}/${GOSS_VARS}" "$tmp_dir" && chmod 644 "$tmp_dir/${GOSS_VARS}"
# Switch between mount or cp files strategy
case "$GOSS_FILES_STRATEGY" in
mount)
info "Starting $CONTAINER_RUNTIME container"
if [ "$CONTAINER_RUNTIME" == "podman" ] && [ $# == 2 ]; then
id=$($CONTAINER_RUNTIME run -d -v "$tmp_dir:/goss:z" "${@:2}" sleep infinity)
else
id=$($CONTAINER_RUNTIME run -d -v "$tmp_dir:/goss:z" "${@:2}")
fi
;;
cp)
info "Creating $CONTAINER_RUNTIME container"
id=$($CONTAINER_RUNTIME create "${@:2}")
image=$($CONTAINER_RUNTIME inspect -f '{{.Config.Image}}' "$id")
info "Container image: $image"
info "Copy goss files into container"
$CONTAINER_RUNTIME cp "$tmp_dir/." "${id}:${CONTAINER_GOSS_PATH}"
info "Starting $CONTAINER_RUNTIME container"
$CONTAINER_RUNTIME start "$id" > /dev/null
;;
*) error "Wrong goss files strategy used! Correct options are \"mount\" or \"cp\"."
esac
$CONTAINER_RUNTIME logs -f "$id" > "$tmp_dir/docker_output.log" 2>&1 &
log_pid=$!
info "Container ID: ${id:0:8}"
}
get_docker_file() {
local cid=$1 # Docker container ID
local src=$2 # Source file path (in the container)
local dst=$3 # Destination file path
if $CONTAINER_RUNTIME exec "${cid}" sh -c "test -e ${src}" > /dev/null; then
mkdir -p "${GOSS_FILES_PATH}"
$CONTAINER_RUNTIME cp "${cid}:${src}" "${dst}"
info "Copied '${src}' from container to '${dst}'"
fi
}
inject_mode() {
info "Inject mode"
info "Container health"
if [ "true" != "$($CONTAINER_RUNTIME inspect -f '{{.State.Running}}' "$id")" ]; then
$CONTAINER_RUNTIME logs "$id" >&2
error "the container failed to start"
fi
info "Running Tests"
if [[ -z "${GOSS_VARS}" ]]; then
$CONTAINER_RUNTIME exec "$id" sh -c "/goss/goss -g /goss/goss.yaml validate $GOSS_OPTS"
else
$CONTAINER_RUNTIME exec "$id" sh -c "/goss/goss -g /goss/goss.yaml --vars='/goss/${GOSS_VARS}' validate $GOSS_OPTS"
fi
}
entrypoint_mode() {
info "Entrypoint mode"
# Berney: wait until goss has finished.
# we will get the exit code later
$CONTAINER_RUNTIME wait "$id" > /dev/null
if [ "true" != "$($CONTAINER_RUNTIME inspect -f '{{.State.Running}}' "$id")" ]; then
$CONTAINER_RUNTIME logs "$id" >&2
# Berney: We expect container to have exited, since we started `goss` directly.
# Propagate exit code - if goss checks pass/failed
exit "$($CONTAINER_RUNTIME inspect -f '{{ .State.ExitCode }}' "$id")"
else
# This generally shouldn't happen
error "Container still running, after wait..."
fi
}
dgoss_run() {
run "$@"
if [[ -e "${GOSS_FILES_PATH}/goss_wait.yaml" ]]; then
info "Found goss_wait.yaml, waiting for it to pass before running tests"
if [[ -z "${GOSS_VARS}" ]]; then
if ! $CONTAINER_RUNTIME exec "$id" sh -c "/goss/goss -g /goss/goss_wait.yaml validate $GOSS_WAIT_OPTS"; then
$CONTAINER_RUNTIME logs "$id" >&2
error "goss_wait.yaml never passed"
fi
else
if ! $CONTAINER_RUNTIME exec "$id" sh -c "/goss/goss -g /goss/goss_wait.yaml --vars='/goss/${GOSS_VARS}' validate $GOSS_WAIT_OPTS"; then
$CONTAINER_RUNTIME logs "$id" >&2
error "goss_wait.yaml never passed"
fi
fi
fi
[[ $GOSS_SLEEP ]] && { info "Sleeping for $GOSS_SLEEP"; sleep "$GOSS_SLEEP"; }
case "$CONTAINER_MODE" in
inject)
inject_mode
;;
entrypoint)
entrypoint_mode
;;
*)
# shellcheck disable=SC2016
error 'Wrong dgoss container mode, only `inject` or `entrypoint`'
;;
esac
}
dgoss_edit() {
run "$@"
info "Run goss add/autoadd to add resources"
# shellcheck disable=SC2016
$CONTAINER_RUNTIME exec -it "$id" sh -c 'cd /goss; PATH="/goss:$PATH" exec sh'
get_docker_file "$id" "/goss/goss.yaml" "${GOSS_FILES_PATH}/${GOSS_FILE:-goss.yaml}"
get_docker_file "$id" "/goss/goss_wait.yaml" "${GOSS_FILES_PATH}/goss_wait.yaml"
if [[ -n "${GOSS_VARS}" ]]; then
get_docker_file "$id" "/goss/${GOSS_VARS}" "${GOSS_FILES_PATH}/${GOSS_VARS}"
fi
}
# Main
main() {
local dgoss_mode=$1
tmp_dir=$(mktemp -d "${DGOSS_TEMP_DIR:-/tmp}"/tmp.XXXXXXXXXX)
chmod 777 "$tmp_dir"
trap 'ret=$?;cleanup;exit $ret' EXIT
GOSS_PATH="${GOSS_PATH:-$(which goss 2> /dev/null || true)}"
[[ $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}
[[ $CONTAINER_RUNTIME =~ ^(docker|podman)$ ]] || { error "Runtime must be one of docker or podman"; }
case "$dgoss_mode" in
run)
dgoss_run "$@"
;;
edit)
dgoss_edit "$@"
;;
*)
error "$USAGE"
esac
}
main "$@"