-
Notifications
You must be signed in to change notification settings - Fork 6
/
sensuflow.sh
executable file
·279 lines (246 loc) · 10.4 KB
/
sensuflow.sh
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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
#!/usr/bin/env ash
###
# shell script to implement SensuFlow
## External dependancies:
# sensuctl: https://sensu.io/downloads
# yq: https://github.com/mikefarah/yq/v4
# jq: https://stedolan.github.io/jq/
#
## Required Environment Variables
# SENSU_API_URL: sensu backend api url used by sensuctl
# SENSU_API_KEY: sensu api key for sensuctl, used instead of user and password above
## Optional Environment Variables
# SENSU_CA: CA certificate as a string
# SENSU_CA_FILE: CA certificate file, if set overrides SENSU_CA
# CONFIGURE_OPTIONS: Additional sensuctl configure options
# NAMESPACES_DIR: directory holding sensuflow namepace subdirectories
# NAMESPACES_FILE: file holding namespace resource definitions sensuflow action should create
# MANAGED_RESOURCES: comma seperated list of resources
# MATCHING_LABEL: resource label to match
# MATCHING_CONDITION: condition to match
# DISABLE_SANITY_CHECKS: if set disable sanity checks
# DISABLE_TLS_VERIFY: if set disable TLS verification
# RESOURCE_AUTHORS: comma separated list of users to filter created_by (default to: sensu_flow)
## Deprecated Authentication Environment Variables
# SENSU_USER: sensu user for sensuctl configue (deprecated, use SENSU_API_KEY)
# SENSU_PASSWORD: sensu password for sensuctl configure (deprecated, use SENSU_API_KEY)
## GitHub Action Notes
# GitHub Actions prefaces variables with INPUT_
# This script maps INPUT_<VAR> to <VAR> for compatibility
## Read in envvars from .env from current directory
if [ -f ./.env ] ; then
source ./.env
fi
### Setup envvar values, including fallback defaults where needed
: ${MATCHING_LABEL:=${INPUT_MATCHING_LABEL:="sensu.io/workflow"}}
: ${MATCHING_CONDITION:=${INPUT_MATCHING_CONDITION:="== 'sensu-flow'"}}
: ${MANAGED_RESOURCES:=${INPUT_MANAGED_RESOURCES:="checks,handlers,filters,mutators,assets,secrets/v1.Secret,roles,role-bindings,core/v2.HookConfig"}}
: ${RESOURCE_AUTHORS:=${INPUT_RESOURCE_AUTHORS:="sensu-flow"}}
: ${NAMESPACES_DIR:=${INPUT_NAMESPACES_DIR:=".sensu/namespaces"}}
: ${NAMESPACES_FILE:=${INPUT_NAMESPACES_FILE:=".sensu/cluster/namespaces.yaml"}}
: ${DISABLE_SANITY_CHECKS:=${INPUT_DISABLE_SANITY_CHECKS:="false"}}
: ${DISABLE_TLS_VERIFY:=${INPUT_DISABLE_TLS_VERIFY:="false"}}
: ${VERBOSE:=${INPUT_VERBOSE:=""}}
: ${SENSU_USER:=${INPUT_SENSU_USER}}
: ${SENSU_PASSWORD:=${INPUT_SENSU_PASSWORD}}
: ${SENSU_API_KEY:=${INPUT_SENSU_API_KEY}}
: ${SENSU_API_URL:=${INPUT_SENSU_API_URL}}
: ${CONFIGURE_ARGS:=${INPUT_CONFIGURE_ARGS}}
: ${SENSU_CA_STRING:=${INPUT_SENSU_CA_STRING}}
: ${SENSU_CA_FILE:=${INPUT_SENSU_CA_FILE}}
if [[ $VERBOSE ]]; then echo "Working Directory: $PWD"; fi
# Check for required envvars to be defined
preflight_check=0
if [ -z "$SENSU_API_KEY" ]; then
[ -z "$SENSU_USER" ] && echo "SENSU_USER environment variable empty" && preflight_check=1
[ -z "$SENSU_PASSWORD" ] && echo "SENSU_PASSWORD environment variable empty" && preflight_check=1
[ $preflight_check -ne 0 ] && echo "Either SENSU_API_KEY or SENSU_USER and SENSU_PASSWORD needs to be specified" && preflight_check=1
fi
[ -z "$SENSU_API_URL" ] && echo "SENSU_API_URL environment variable empty" && preflight_check=1
[ -z "$MATCHING_LABEL" ] && echo "MATCHING_LABEL environment variable empty" && preflight_check=1
[ -z "$MATCHING_CONDITION" ] && echo "MATCHING_CONDITION environment variable empty" && preflight_check=1
[ -z "$MANAGED_RESOURCES" ] && echo "MANAGED_RESOURCES environment variable empty" && preflight_check=1
[ -z "$NAMESPACES_DIR" ] && echo "NAMESPACES_DIR environment variable empty" && preflight_check=1
[ -z "$RESOURCE_AUTHORS" ] && echo "RESOURCE_AUTHORS environment variable empty" && preflight_check=1
if test $preflight_check -ne 0 ; then
echo "Missing environment variables"
exit 1
else
if [[ $VERBOSE ]]; then echo "All needed environment variables are available"; fi
fi
LABEL_SELECTOR="${MATCHING_LABEL} ${MATCHING_CONDITION}"
if [ -z "$SENSU_CA_STRING" ] ; then
touch /tmp/sensu_ca.pem
else
echo $SENSU_CA_STRING > /tmp/sensu_ca.pem
fi
: ${SENSU_CA_FILE:="/tmp/sensu_ca.pem"}
if [ -s $SENSU_CA_FILE ]; then
if [[ $VERBOSE ]]; then echo "custom CA file present"; fi
CA_ARG="--trusted-ca-file ${SENSU_CA_FILE}"
else
CA_ARG=''
fi
if [ "$DISABLE_TLS_VERIFY" = "false" ]; then
DISABLE_TLS_VERIFY=""
fi
if [ -z "$DISABLE_TLS_VERIFY" ]; then
if [[ $VERBOSE ]]; then echo "tls verification enabled"; fi
curl_command="curl "
else
if [[ $VERBOSE ]]; then echo "tls verification disabled"; fi
curl_command="curl -k"
fi
if [[ $VERBOSE ]]; then echo "Checking Sensu readiness"; fi
status=$(${curl_command} --connect-timeout 30 -s -o /dev/null -w "%{http_code}" "$SENSU_API_URL/health")
if [ $status -lt 200 ] || [ $status -ge 400 ]; then
echo "Sensu Backend does not appear to be ready"
echo "Probe of "$SENSU_API_URL/health" returned status code: $status"
exit 1
fi
if [ -z "$SENSU_API_KEY" ]; then # Skip auth test if API KEY is specified
if [[ $VERBOSE ]]; then echo "Checking Sensu auth credentials"; fi
status=$(${curl_command} --connect-timeout 30 -k -o /dev/null -s -w "%{http_code}" -X GET "${SENSU_API_URL}/auth/test" -u "${SENSU_USER}:${SENSU_PASSWORD}")
if [ $status -lt 200 ] || [ $status -ge 400 ]; then
echo "Sensu auth failed"
echo "Probe of "$SENSU_API_URL/auth/test" returned status code: $status"
exit 1
fi
else
export SENSU_API_KEY
fi
if [ "$DISABLE_SANITY_CHECKS" = "false" ]; then
DISABLE_SANITY_CHECKS=""
fi
if [ -z "$DISABLE_SANITY_CHECKS" ]; then
if [[ $VERBOSE ]]; then echo "sanity checks enabled"; fi
else
if [[ $VERBOSE ]]; then echo "sanity checks disabled"; fi
fi
if [ -z "$SENSU_API_KEY" ]; then # Disable sensuctl configure if API KEY is specified, use environment
if [[ $VERBOSE ]]; then echo "Configuring sensuctl:"; fi
sensuctl configure -n --username ${SENSU_USER} --password ${SENSU_PASSWORD} --url ${SENSU_API_URL} ${CA_ARG} ${CONFIGURE_OPTIONS}
retval=$?
sensuctl config view
if test $retval -ne 0; then
echo "sensuctl configure failed"
exit $retval
fi
else
export SENSU_API_URL="$SENSU_API_URL"
fi
if [[ $VERBOSE ]]; then
echo "Current Directory:"
pwd
echo "Executing Sensuflow"
echo "Matching Label: ${MATCHING_LABEL}"
echo "Matching Condition: ${MATCHING_CONDITION}"
echo "Label Selector: ${LABEL_SELECTOR}"
echo "Resource Authors: ${RESOURCE_AUTHORS}"
fi
# Functions
# Display error message and exit
function die {
echo "$1"
exit 1
}
# Check if a namespace exists
function is_namespace {
if [[ $VERBOSE ]]; then echo "checking to see if namespace exists on server"; fi
QUERY=$(sensuctl namespace list --format json | jq -r ".[] | select(.name==\"${1}\") | .name")
test "${1}" = "${QUERY}"
return $?
}
function lint_resource_metadata {
resource_dir=$1
required_label=$2
allowed_namespace=$3
if [[ $VERBOSE ]]; then echo "linting resource metadata in $resource_dir"; fi
yaml_files=$(find $resource_dir -name "*.y?ml")
for file in $yaml_files; do
if [[ $required_label ]]; then
bad_labels=$(yq -N e ".metadata.labels | has(\"${required_label}\")" $file | grep -c 'false' )
if [ $bad_labels -ne 0 ] ; then die "resource in $file may be missing label $MATCHING_LABEL" ; fi
bad_labels=$(yq -N e ".metadata.labels[\"${required_label}\"] == null" $file | grep -c 'true' )
if [ $bad_labels -ne 0 ] ; then die "resource in $file may be missing label $MATCHING_LABEL" ; fi
fi
result=$(yq -N e '.metadata.namespace' $file)
for line in $result; do
if [ $line != "null" ]; then
if [[ $allowed_namespace ]]; then
if [ $line != $allowed_namespace ]; then die "resource in $file has metadata.namespace defined as $line" ; fi
else
if [[ $line ]]; then die "resource in $file has metadata.namespace defined as $line" ; fi
fi
fi
done
done
json_files=$(find $resource_dir -name "*.json")
for file in $json_files ; do
if [[ $required_label ]]; then
bad_labels=$(jq ".metadata.labels[\"${required_label}\"] == null" $file | grep -c 'true')
if [ $bad_labels -ne 0 ] ; then die "resource in $file may be missing label $MATCHING_LABEL" ; fi
fi
result=$(jq '.metadata.namespace' $file)
for line in $result; do
if [ $line != 'null' ]; then
if [[ $allowed_namespace ]]; then
if [ $line != \"$allowed_namespace\" ]; then die "Error resource in $file has metadata.namespace defined as $line instead of \"$allowed_namespace\"" ; fi
else
if [[ $line ]]; then die "resource in $file has metadata.namespace defined as $line" ; fi
fi
fi
done
done
}
# Main
# First, make sure we have our namespaces
if test -f ${NAMESPACES_FILE}
then
yq -N e '.' ${NAMESPACES_FILE} > /dev/null || die "$NAMESPACES_FILE is not valid yaml"
bad_resource_count=$(yq -N e '.type' ${NAMESPACES_FILE} | grep -c -v "Namespace")
if test $bad_resource_count -ne 0; then die "${NAMESPACES_FILE} has non Namespace Sensu Resources defined" ; fi
sensuctl create -f ${NAMESPACES_FILE} || die "sensuctl error creating namespaces file"
fi
cd $NAMESPACES_DIR || die "Failed to cd to namespaces directory!"
if [[ $VERBOSE ]]; then echo "Namespaces Directory: $(pwd)"; fi
for namespace in $(ls -1)
do
# If not a directory of resources then skip
if ! test -d ${namespace}
then
echo "${namespace} in ${NAMESPACES_DIR}/ is not a directory, skipping"
continue
fi
if [ -z $DISABLE_SANITY_CHECKS ]; then lint_resource_metadata ${namespace} ${MATCHING_LABEL} ${namespace}; fi
if ! is_namespace ${namespace}
then
# Skip or die?
# Skip means this may pass silently, die would cause bad exit
# that should cause a build failure
echo "Directory ${namespace} exists in namespaces/ but is not a defined namespace in sensu, skipping"
continue
fi
echo "Namespace ${namespace}"
echo -e "Pruning resources...\n"
if [[ $VERBOSE ]]
then
echo -e "sensuctl prune ${MANAGED_RESOURCES} --namespace ${namespace} --label-selector \"${LABEL_SELECTOR}\" -r -f ${namespace} | jq '. | length'"
fi
num=$(sensuctl prune ${MANAGED_RESOURCES} --namespace ${namespace} --label-selector "${LABEL_SELECTOR}" -r -f ${namespace} --users ${RESOURCE_AUTHORS} | jq '. | length')
retval=$?
if test $retval -ne 0; then
echo "Error during sensuctl prune!"
exit 1
fi
echo "${num} resources deleted"
echo -e "Creating/Updating resources...\c"
# Would be really nice if this gave us some type of output
sensuctl create -r -f ${namespace} --namespace ${namespace}
retval=$?
if test $retval -ne 0; then
echo "Error during sensuctl create!"
exit 1
fi
echo -e "Done\n"
done