Skip to content

Commit 862e7d0

Browse files
authored
Merge pull request #10574 from maage/bats-1
bash_replace_or_append fixes
2 parents 267401c + 628c5fb commit 862e7d0

File tree

7 files changed

+1154
-145
lines changed

7 files changed

+1154
-145
lines changed

shared/macros/10-bash.jinja

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,6 +1465,22 @@ cce="{{{ cce_identifiers['cce'] }}}"
14651465
{{%- endmacro -%}}
14661466

14671467

1468+
{{#
1469+
Ensure file ends with newline
1470+
1471+
Do not modify file at all if there already is newline. Always follows
1472+
symlinks.
1473+
1474+
:param file: file to check
1475+
#}}
1476+
{{%- macro bash_ensure_nl_at_eof(file) -%}}
1477+
{{#- Plain sed '$a\' updates stat even if it dones not change the file. -#}}
1478+
if [[ -s "{{{ file }}}" ]] && [[ -n "$(tail -c 1 -- "{{{ file }}}" || true)" ]]; then
1479+
LC_ALL=C sed -i --follow-symlinks '$a'\\ "{{{ file }}}"
1480+
fi
1481+
{{%- endmacro -%}}
1482+
1483+
14681484
{{#
14691485
Macro to replace configuration setting in config file or add the configuration setting if
14701486
it does not exist.
@@ -1491,14 +1507,6 @@ cce="{{{ cce_identifiers['cce'] }}}"
14911507

14921508
#}}
14931509
{{%- macro bash_replace_or_append(config_file, key, value, format='%s = %s') -%}}
1494-
1495-
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
1496-
# Otherwise, regular sed command will do.
1497-
sed_command=('sed' '-i')
1498-
if test -L "{{{ config_file }}}"; then
1499-
sed_command+=('--follow-symlinks')
1500-
fi
1501-
15021510
# Strip any search characters in the key arg so that the key can be replaced without
15031511
# adding any search characters to the config file.
15041512
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "{{{ key }}}")
@@ -1511,12 +1519,12 @@ printf -v formatted_output "{{{ format }}}" "$stripped_key" "{{{ value }}}"
15111519
# so if we search for 'setting', 'setting2' won't match.
15121520
if LC_ALL=C grep -q -m 1 -i -e "{{{ key }}}\\>" "{{{ config_file }}}"; then
15131521
escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output")
1514-
"${sed_command[@]}" "s/{{{ key }}}\\>.*/$escaped_formatted_output/gi" "{{{ config_file }}}"
1522+
LC_ALL=C sed -i --follow-symlinks "s/{{{ key }}}\\>.*/$escaped_formatted_output/gi" "{{{ config_file }}}"
15151523
else
1516-
# \n is precaution for case where file ends without trailing newline
1517-
{{% if cce_identifiers and 'cce' in cce_identifiers -%}}
1524+
{{{ bash_ensure_nl_at_eof(config_file) | indent }}}
1525+
{{%- if cce_identifiers and 'cce' in cce_identifiers %}}
15181526
{{{ set_cce_value() }}}
1519-
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "{{{ config_file }}}" >> "{{{ config_file }}}"
1527+
printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "{{{ config_file }}}" >> "{{{ config_file }}}"
15201528
{{%- endif %}}
15211529
printf '%s\n' "$formatted_output" >> "{{{ config_file }}}"
15221530
fi

tests/unit/bash/bash_os_linux_conditional.bats.jinja

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,46 @@
11
#!/bin/bash
22

3-
@test "bash_os_linux_conditional - test OS release - RHEL" {
4-
os_release_path="$(mktemp)"
3+
set -pu
4+
5+
is_old_bats=0
6+
7+
setup() {
8+
if [[ -z "${BATS_TEST_TMPDIR:-}" ]] || [[ ! -d "${BATS_TEST_TMPDIR}" ]]; then
9+
BATS_TEST_TMPDIR="$(mktemp -d)" # 1.4.0
10+
# shellcheck disable=SC2034
11+
BATS_TEARDOWN_STARTED= # 1.3.0
12+
is_old_bats=1
13+
else
14+
is_old_bats=0
15+
fi
16+
pushd "${BATS_TEST_TMPDIR}" || exit 1
17+
os_release_path="os-release"
18+
}
19+
20+
teardown() {
21+
if (( is_old_bats )); then
22+
if [[ -z "${BATS_TEST_TMPDIR:-}" ]] || [[ ! -d "${BATS_TEST_TMPDIR}" ]]; then
23+
>&2 echo "INTERNAL ERROR"
24+
exit 3
25+
fi
26+
local tmppath xpwd
27+
tmppath="$(readlink -f -- "${BATS_TEST_TMPDIR}")"
28+
if [[ ! "${tmppath}" =~ ^/tmp/ ]] || [[ ! -d "${tmppath}" ]]; then
29+
>&2 echo "INTERNAL ERROR"
30+
exit 3
31+
fi
32+
xpwd="$(readlink -f -- .)"
33+
if [[ "${tmppath}" != "${xpwd}" ]]; then
34+
>&2 echo "INTERNAL ERROR"
35+
exit 3
36+
fi
37+
popd || exit 1
38+
rm -rf -- "${tmppath}"
39+
BATS_TEST_TMPDIR=""
40+
fi
41+
}
542

43+
@test "bash_os_linux_conditional - test OS release - RHEL" {
644
cat << EOF > "$os_release_path"
745
NAME="Red Hat Enterprise Linux"
846
VERSION="9.2 (Plow)"
@@ -42,13 +80,9 @@ EOF
4280
! ( {{{ bash_os_linux_conditional("fedora", "38", "==", "$os_release_path") }}} )
4381
! ( {{{ bash_os_linux_conditional("fedora", "9.2", "==", "$os_release_path") }}} )
4482
! ( {{{ bash_os_linux_conditional("fedora", "9.4", "<", "$os_release_path") }}} )
45-
46-
rm -rf "$os_release_path"
4783
}
4884

4985
@test "bash_os_linux_conditional - test OS release - Ubuntu" {
50-
os_release_path="$(mktemp)"
51-
5286
cat << EOF > "$os_release_path"
5387
PRETTY_NAME="Ubuntu 22.04.1 LTS"
5488
NAME="Ubuntu"
@@ -82,6 +116,4 @@ EOF
82116

83117
! ( {{{ bash_os_linux_conditional("fedora", "38", "==", "$os_release_path") }}} )
84118
! ( {{{ bash_os_linux_conditional("fedora", "22.10", "<", "$os_release_path") }}} )
85-
86-
rm -rf "$os_release_path"
87119
}

tests/unit/bash/bash_pkg_conditional_rpm.bats.jinja

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#!/bin/bash
22

3+
set -pu
4+
35
# Mock the "rpm" command
46
# mocked package: coconut-4.5.17
57
# The mock is used to avoid the need to have a specific RPM package in a

tests/unit/bash/execute_tests.sh

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,90 @@
11
#!/bin/bash
22

3-
PYTHON_EXECUTABLE="$1"
4-
TESTS_ROOT="$2"
5-
TESTDIR="$3"
6-
OUTDIR="$4"
3+
set -epu
74

8-
mkdir -p "$OUTDIR"
5+
usage() {
6+
printf "Usage: %s [OPTIONS] PYTHON_EXECUTABLE TESTS_ROOT TESTDIR OUTDIR [bats opts]" "${0##/}"
7+
printf " %s [--help]" "${0##/}"
8+
printf "\nOPTIONS\n"
9+
printf " --verbose\n"
10+
printf " --quiet\n"
11+
printf " --debug\n"
12+
printf " --parallel | --no-parallel\n"
13+
printf " --\n"
14+
}
915

10-
PYTHONPATH="$TESTS_ROOT/.." ${PYTHON_EXECUTABLE} "$TESTS_ROOT/../build-scripts/expand_jinja.py" --outdir "$OUTDIR" $TESTDIR/*.jinja && bats "$OUTDIR"
16+
OPT_debug=0
17+
OPT_parallel=0
18+
if [[ -x /usr/bin/parallel ]]; then
19+
OPT_parallel=1
20+
fi
21+
22+
OPT_verbose=1
23+
while (( $# )); do
24+
case "$1" in
25+
--verbose) (( OPT_verbose++, 1 )); shift ;;
26+
--quiet) OPT_verbose=0; shift ;;
27+
--debug) set -x; OPT_debug=1; shift ;;
28+
29+
--parallel) OPT_parallel=1; shift ;;
30+
--no[_-]parallel) OPT_parallel=0; shift ;;
31+
32+
--help) usage; exit 2 ;;
33+
--) shift; break ;;
34+
35+
*) break ;;
36+
esac
37+
done
38+
39+
bats_version="$(bats -v)" || :
40+
case "${bats_version##* }" in
41+
# Debian 10 v0.4.0
42+
# Usage: bats [-c] [-p | -t]
43+
""|"0."*|"1.0."*|"1.1."*)
44+
OPT_parallel=0
45+
OPT_verbose=0
46+
OPT_debug=0
47+
;;
48+
# Ubuntu 22.04
49+
# Error: Bad command line option '--print-output-on-failure'
50+
"1.2."*|"1.3."*|"1.4."*)
51+
OPT_verbose=0
52+
OPT_debug=0
53+
;;
54+
esac
55+
56+
PYTHON_EXECUTABLE="$1"; shift
57+
TESTS_ROOT="$1"; shift
58+
TESTDIR="$1"; shift
59+
OUTDIR="$1"; shift
60+
61+
mkdir -p "${OUTDIR}"
62+
63+
bats_opts=()
64+
65+
if (( OPT_parallel )); then
66+
bats_opts+=(--jobs "$(nproc)") # 1.2.0
67+
fi
68+
69+
if (( OPT_verbose > 1 )); then
70+
bats_opts+=(--verbose-run) # 1.5.0
71+
elif (( OPT_verbose == 1 )); then
72+
bats_opts+=(--print-output-on-failure) # 1.5.0
73+
fi
74+
75+
if (( OPT_debug )); then
76+
bats_opts+=(
77+
--no-tempdir-cleanup # 1.4.0
78+
--trace # 1.5.0
79+
)
80+
fi
81+
82+
rc=0
83+
PYTHONPATH="${TESTS_ROOT}/.." ${PYTHON_EXECUTABLE} \
84+
"${TESTS_ROOT}/../build-scripts/expand_jinja.py" --outdir "${OUTDIR}" \
85+
"${TESTDIR}"/*.jinja || rc=$?
86+
if (( rc )); then
87+
>&2 echo "ERROR: expand_jinja.py could not create bats files"
88+
exit "${rc}"
89+
fi
90+
bats "${bats_opts[@]}" "$@" "${OUTDIR}"
Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,60 @@
11
#!/bin/bash
22

3+
set -pu
34

45
function call_bash_ensure_ini_config {
56
{{{ bash_ensure_ini_config("$1", "$2", "$3", "$4") | indent(4) }}}
67
}
78

9+
is_old_bats=0
10+
11+
setup() {
12+
if [[ -z "${BATS_TEST_TMPDIR:-}" ]] || [[ ! -d "${BATS_TEST_TMPDIR}" ]]; then
13+
BATS_TEST_TMPDIR="$(mktemp -d)" # 1.4.0
14+
# shellcheck disable=SC2034
15+
BATS_TEARDOWN_STARTED= # 1.3.0
16+
is_old_bats=1
17+
else
18+
is_old_bats=0
19+
fi
20+
pushd "${BATS_TEST_TMPDIR}" || exit 1
21+
mkdir -p sssd_test
22+
}
23+
24+
teardown() {
25+
if (( is_old_bats )); then
26+
if [[ -z "${BATS_TEST_TMPDIR:-}" ]] || [[ ! -d "${BATS_TEST_TMPDIR}" ]]; then
27+
>&2 echo "INTERNAL ERROR"
28+
exit 3
29+
fi
30+
local tmppath xpwd
31+
tmppath="$(readlink -f -- "${BATS_TEST_TMPDIR}")"
32+
if [[ ! "${tmppath}" =~ ^/tmp/ ]] || [[ ! -d "${tmppath}" ]]; then
33+
>&2 echo "INTERNAL ERROR"
34+
exit 3
35+
fi
36+
xpwd="$(readlink -f -- .)"
37+
if [[ "${tmppath}" != "${xpwd}" ]]; then
38+
>&2 echo "INTERNAL ERROR"
39+
exit 3
40+
fi
41+
popd || exit 1
42+
rm -rf -- "${tmppath}"
43+
BATS_TEST_TMPDIR=""
44+
fi
45+
}
46+
847
@test "bash_ensure_ini_config - Basic value remediation" {
9-
mkdir sssd_test
1048
printf "[pam]\npam_cert_auth = false\n" > sssd_test/sssd.conf
1149
expected_output="[pam]\npam_cert_auth = true\n"
1250

1351
call_bash_ensure_ini_config "sssd_test/sssd.conf" "pam" "pam_cert_auth" "true"
1452

1553
run diff "sssd_test/sssd.conf" <(printf "$expected_output")
1654
[ "$status" -eq 0 ]
17-
18-
rm -rf sssd_test
1955
}
2056

2157
@test "bash_ensure_ini_config - Value remediation in multiple files" {
22-
mkdir sssd_test
2358
printf "[pam]\npam_cert_auth = false\n" > sssd_test/sssd.conf
2459
printf "[pam]\npam_cert_auth = false\n" > pam_cert_auth.conf
2560
expected_output="[pam]\npam_cert_auth = true\n"
@@ -31,35 +66,26 @@ function call_bash_ensure_ini_config {
3166

3267
run diff "pam_cert_auth.conf" <(printf "$expected_output")
3368
[ "$status" -eq 0 ]
34-
35-
rm pam_cert_auth.conf
36-
rm -rf sssd_test
3769
}
3870

3971
@test "bash_ensure_ini_config - No remediation happened" {
40-
mkdir sssd_test
4172
printf "[pam]\npam_cert_auth = true\n" > sssd_test/sssd.conf
4273
expected_output="[pam]\npam_cert_auth = true\n"
4374

4475
call_bash_ensure_ini_config "sssd_test/sssd.conf" "pam" "pam_cert_auth" "true"
4576

4677
run diff "sssd_test/sssd.conf" <(printf "$expected_output")
4778
[ "$status" -eq 0 ]
48-
49-
rm -rf sssd_test
5079
}
5180

5281
@test "bash_ensure_ini_config - Append section with option to empty file" {
53-
mkdir sssd_test
5482
printf "" > sssd_test/sssd.conf
5583
expected_output="[pam]\npam_cert_auth = true\n"
5684

5785
call_bash_ensure_ini_config "sssd_test/sssd.conf" "pam" "pam_cert_auth" "true"
5886

5987
run diff "sssd_test/sssd.conf" <(printf "$expected_output")
6088
[ "$status" -eq 0 ]
61-
62-
rm -rf sssd_test
6389
}
6490

6591
@test "bash_ensure_ini_config - Create file with section and option" {
@@ -69,40 +95,30 @@ function call_bash_ensure_ini_config {
6995

7096
run diff "sssd_test/sssd.conf" <(printf "$expected_output")
7197
[ "$status" -eq 0 ]
72-
73-
rm -rf sssd_test
7498
}
7599

76100
@test "bash_ensure_ini_config - Append option to section" {
77-
mkdir sssd_test
78101
printf "[pam]\n" > sssd_test/sssd.conf
79102
expected_output="[pam]\npam_cert_auth = true\n"
80103

81104
call_bash_ensure_ini_config "sssd_test/sssd.conf" "pam" "pam_cert_auth" "true"
82105

83106
run diff "sssd_test/sssd.conf" <(printf "$expected_output")
84107
[ "$status" -eq 0 ]
85-
86-
rm -rf sssd_test
87108
}
88109

89110
@test "bash_ensure_ini_config - Append option to section when section is substring of option" {
90-
mkdir sssd_test
91111
printf "[pam]\n" > sssd_test/sssd.conf
92112
expected_output="[pam]\npam_verbosity = 1\npam_cert_auth = true\n"
93113

94114
call_bash_ensure_ini_config "sssd_test/sssd.conf" "pam" "pam_cert_auth" "true"
95115
call_bash_ensure_ini_config "sssd_test/sssd.conf" "pam" "pam_verbosity" "1"
96116

97117
run diff "sssd_test/sssd.conf" <(printf "$expected_output")
98-
99-
rm -rf sssd_test
100-
101118
[ "$status" -eq 0 ]
102119
}
103120

104121
@test "bash_ensure_ini_config - Append option to section in multiple files" {
105-
mkdir sssd_test
106122
printf "[pam]\n" > sssd_test/sssd.conf
107123
printf "[pam]\n" > pam_cert_auth.conf
108124
expected_output="[pam]\npam_cert_auth = true\n"
@@ -111,23 +127,17 @@ function call_bash_ensure_ini_config {
111127

112128
run diff "sssd_test/sssd.conf" <(printf "$expected_output")
113129
[ "$status" -eq 0 ]
114-
130+
115131
run diff "pam_cert_auth.conf" <(printf "$expected_output")
116132
[ "$status" -eq 0 ]
117-
118-
rm pam_cert_auth.conf
119-
rm -rf sssd_test
120133
}
121134

122135
@test "bash_ensure_ini_config - Append section with option to non-empty file" {
123-
mkdir sssd_test
124136
printf "[section]\nkey = value\n" > sssd_test/sssd.conf
125137
expected_output="[section]\nkey = value\n[pam]\npam_cert_auth = true\n"
126138

127139
call_bash_ensure_ini_config "sssd_test/sssd.conf" "pam" "pam_cert_auth" "true"
128140

129141
run diff "sssd_test/sssd.conf" <(printf "$expected_output")
130142
[ "$status" -eq 0 ]
131-
132-
rm -rf sssd_test
133143
}

0 commit comments

Comments
 (0)