1#!/usr/bin/env bash
2set -e
3
4if [ -d /var/cfengine ]; then
5    rm -rf /var/cfengine
6fi
7
8# Test assumes we start in core or masterfiles directory
9cd ../
10
11if [ ! -d core ]; then
12    echo "Cloning core (master)"
13    git clone --recursive https://github.com/cfengine/core.git
14fi
15
16if [ ! -d masterfiles ]; then
17    echo "Cloning masterfiles (master)"
18    git clone --recursive https://github.com/cfengine/masterfiles.git
19fi
20
21echo "Checking for systemctl"
22systemctl --version
23
24cd core/
25echo "Building CFEngine core"
26set +e
27git fetch --unshallow 2>&1 >> /dev/null
28git remote add upstream https://github.com/cfengine/core.git  \
29    && git fetch upstream 'refs/tags/*:refs/tags/*' 2>&1 >> /dev/null
30set -e
31
32./autogen.sh --enable-debug --with-systemd-service
33make
34
35echo "Installing CFEngine core"
36make install
37cd ../
38
39cd masterfiles/
40./autogen.sh
41echo "Installing CFEngine masterfiles"
42make install
43
44systemctl daemon-reload
45
46function print_ps {
47    set +e
48    echo "CFEngine processes:"
49    ps aux | grep [c]f-
50
51    echo "Valgrind processes:"
52    ps aux | grep [v]algrind
53    set -e
54}
55
56function no_errors {
57    set +e
58    grep -i "error" $1
59    grep -i "error" $1 && exit 1
60    set -e
61}
62
63function check_daemon_output {
64    echo "Examining $1:"
65    no_errors $1
66}
67
68function check_output {
69    set -e
70    if [ ! -f "$1" ]; then
71        echo "$1 does not exists!"
72        exit 1
73    fi
74    echo "Looking for problems in $1:"
75    grep -i "ERROR SUMMARY: 0 errors" "$1"
76    cat $1 | sed -e "/ 0 errors/d" -e "/and suppressed error/d" -e "/a memory error detector/d" -e "/This database contains unknown binary data/d" > filtered.txt
77    no_errors filtered.txt
78    set +e
79    grep -i "at 0x" filtered.txt
80    grep -i "at 0x" filtered.txt && exit 1
81    grep -i "by 0x" filtered.txt
82    grep -i "by 0x" filtered.txt && exit 1
83    grep -i "Failed to connect" filtered.txt
84    grep -i "Failed to connect" filtered.txt && exit 1
85    set -e
86}
87
88function check_serverd_valgrind_output {
89    if [ ! -f "$1" ]; then
90        echo "$1 does not exists!"
91        exit 1
92    fi
93    set -e
94    echo "Serverd has 1 expected valgrind error in travis because of old glibc"
95    echo "Because of this we use special assertions on output"
96    echo "Looking for problems in $1:"
97    grep -i "definitely lost" $1
98    grep -i "indirectly lost" $1
99    grep -i "ERROR SUMMARY" $1
100    grep -i "definitely lost: 0 bytes in 0 blocks" $1
101    grep -i "indirectly lost: 0 bytes in 0 blocks" $1
102    grep -i "ERROR SUMMARY: 0 errors" "$1"
103
104    cat $1 | sed -e "/ERROR SUMMARY/d" -e "/and suppressed error/d" -e "/a memory error detector/d" > filtered.txt
105
106    no_errors filtered.txt
107    set +e
108    grep -i "at 0x" filtered.txt
109    grep -i "at 0x" filtered.txt && exit 1
110    grep -i "by 0x" filtered.txt
111    grep -i "by 0x" filtered.txt && exit 1
112    set -e
113}
114
115function check_masterfiles_and_inputs {
116    set -e
117    echo "Comparing promises.cf from inputs and masterfiles:"
118    diff /var/cfengine/inputs/promises.cf /var/cfengine/masterfiles/promises.cf
119}
120
121/var/cfengine/bin/cf-agent --version
122
123VG_OPTS="--leak-check=full --track-origins=yes --error-exitcode=1"
124BOOTSTRAP_IP="$(ifconfig | grep -A1 Ethernet | sed '2!d;s/.*addr:\([0-9.]*\).*/\1/')"
125
126valgrind $VG_OPTS /var/cfengine/bin/cf-key 2>&1 | tee cf-key.txt
127check_output cf-key.txt
128valgrind $VG_OPTS /var/cfengine/bin/cf-agent -B $BOOTSTRAP_IP 2>&1 | tee bootstrap.txt
129check_output bootstrap.txt
130
131# Validate all databases here, because later, we cannot validate
132# cf_lastseen.lmdb:
133echo "Running cf-check diagnose --validate on all databases:"
134valgrind $VG_OPTS /var/cfengine/bin/cf-check diagnose --validate 2>&1 | tee cf_check_validate_all.txt
135check_output cf_check_validate_all.txt
136
137check_masterfiles_and_inputs
138
139print_ps
140
141echo "Stopping service to relaunch under valgrind"
142systemctl stop cfengine3
143sleep 10
144print_ps
145
146# The IP we bootstrapped to cannot actually be used for communication.
147# This ensures that cf-serverd binds to loopback interface, and cf-net
148# connects to it:
149echo "127.0.0.1" > /var/cfengine/policy_server.dat
150
151echo "Starting cf-serverd with valgrind in background:"
152valgrind $VG_OPTS --log-file=serverd.txt /var/cfengine/bin/cf-serverd --no-fork 2>&1 > serverd_output.txt &
153server_pid="$!"
154sleep 20
155
156echo "Starting cf-execd with valgrind in background:"
157valgrind $VG_OPTS --log-file=execd.txt /var/cfengine/bin/cf-execd --no-fork 2>&1 > execd_output.txt &
158exec_pid="$!"
159sleep 10
160
161print_ps
162
163echo "Running cf-net:"
164valgrind $VG_OPTS /var/cfengine/bin/cf-net GET /var/cfengine/masterfiles/promises.cf 2>&1 | tee get.txt
165check_output get.txt
166
167echo "Checking promises.cf diff (from cf-net GET):"
168diff ./promises.cf /var/cfengine/masterfiles/promises.cf
169
170echo "Running update.cf:"
171valgrind $VG_OPTS /var/cfengine/bin/cf-agent -K -f update.cf 2>&1 | tee update.txt
172check_output update.txt
173check_masterfiles_and_inputs
174echo "Running update.cf without local copy:"
175valgrind $VG_OPTS /var/cfengine/bin/cf-agent -K -f update.cf -D mpf_skip_local_copy_optimization 2>&1 | tee update2.txt
176check_output update2.txt
177check_masterfiles_and_inputs
178echo "Running promises.cf:"
179valgrind $VG_OPTS /var/cfengine/bin/cf-agent -K -f promises.cf 2>&1 | tee promises.txt
180check_output promises.txt
181
182# Dump all databases, use grep to filter the JSON lines
183# (optional whitespace then double quote or curly brackets).
184# Some of the databases have strings containing "error"
185# which check_output greps for.
186echo "Running cf-check dump:"
187valgrind $VG_OPTS /var/cfengine/bin/cf-check dump 2>&1 | grep -E '\s*[{}"]' --invert-match | tee cf_check_dump.txt
188check_output cf_check_dump.txt
189
190echo "Running cf-check diagnose on all databases"
191valgrind $VG_OPTS /var/cfengine/bin/cf-check diagnose 2>&1 | tee cf_check_diagnose.txt
192check_output cf_check_diagnose.txt
193
194# Because of the hack with bootstrap IP / policy_server.dat above
195# lastseen would not pass validation:
196echo "Running cf-check diagnose --validate on all databases except cf_lastseen.lmdb:"
197find /var/cfengine/state -name '*.lmdb' ! -name 'cf_lastseen.lmdb' -exec \
198    valgrind $VG_OPTS /var/cfengine/bin/cf-check diagnose --validate {} + 2>&1 \
199    | tee cf_check_validate_no_lastseen.txt
200check_output cf_check_validate_no_lastseen.txt
201
202echo "Checking that bootstrap ID doesn't change"
203/var/cfengine/bin/cf-agent --show-evaluated-vars | grep bootstrap_id > id_a
204/var/cfengine/bin/cf-agent -K --show-evaluated-vars | grep bootstrap_id > id_b
205cat id_a
206diff id_a id_b
207
208echo "Checking that bootstrap ID has expected length"
209[ `cat id_a | awk '{print $2}' | wc -c` -eq 41 ]
210
211print_ps
212
213echo "Checking that serverd and execd PIDs are still correct/alive:"
214ps -p $exec_pid
215ps -p $server_pid
216
217echo "Killing valgrind cf-execd"
218kill $exec_pid
219echo "Killing valgrind cf-serverd"
220kill $server_pid
221sleep 30
222
223echo "Output from cf-execd in valgrind:"
224cat execd.txt
225check_output execd.txt
226check_daemon_output execd_output.txt
227
228echo "Output from cf-serverd in valgrind:"
229cat serverd.txt
230check_serverd_valgrind_output serverd.txt
231check_daemon_output serverd_output.txt
232
233echo "Stopping cfengine3 service"
234systemctl stop cfengine3
235
236echo "Done killing"
237sleep 10
238print_ps
239
240echo "Check that bootstrap was successful"
241grep "This host assumes the role of policy server" bootstrap.txt
242grep "completed successfully!" bootstrap.txt
243
244echo "valgrind_health_check successful! (valgrind.sh)"
245