Skip to content

Commit 4febd32

Browse files
authored
Merge pull request #548 from buchdag/cow
Use cp+rm / sed+cp+rm instead of mv / sed -i on location configuration
2 parents 6b07d6b + 36ef610 commit 4febd32

File tree

4 files changed

+163
-13
lines changed

4 files changed

+163
-13
lines changed

app/functions.sh

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,34 @@ fi
2525

2626
function add_location_configuration {
2727
local domain="${1:-}"
28+
# If no domain was passed or if the domain has no custom conf, use default instead
2829
[[ -z "$domain" || ! -f "${VHOST_DIR}/${domain}" ]] && domain=default
29-
[[ -f "${VHOST_DIR}/${domain}" && \
30-
-n $(sed -n "/$START_HEADER/,/$END_HEADER/p" "${VHOST_DIR}/${domain}") ]] && return 0
31-
echo "$START_HEADER" > "${VHOST_DIR}/${domain}".new
32-
cat /app/nginx_location.conf >> "${VHOST_DIR}/${domain}".new
33-
echo "$END_HEADER" >> "${VHOST_DIR}/${domain}".new
34-
[[ -f "${VHOST_DIR}/${domain}" ]] && cat "${VHOST_DIR}/${domain}" >> "${VHOST_DIR}/${domain}".new
35-
mv -f "${VHOST_DIR}/${domain}".new "${VHOST_DIR}/${domain}"
36-
return 1
30+
31+
if [[ -f "${VHOST_DIR}/${domain}" && -n $(sed -n "/$START_HEADER/,/$END_HEADER/p" "${VHOST_DIR}/${domain}") ]]; then
32+
# If the config file exist and already have the location configuration, end with exit code 0
33+
return 0
34+
else
35+
# Else write the location configuration to a temp file ...
36+
echo "$START_HEADER" > "${VHOST_DIR}/${domain}".new
37+
cat /app/nginx_location.conf >> "${VHOST_DIR}/${domain}".new
38+
echo "$END_HEADER" >> "${VHOST_DIR}/${domain}".new
39+
# ... append the existing file content to the temp one ...
40+
[[ -f "${VHOST_DIR}/${domain}" ]] && cat "${VHOST_DIR}/${domain}" >> "${VHOST_DIR}/${domain}".new
41+
# ... and copy the temp file to the old one (if the destination file is bind mounted, you can't change
42+
# its inode from within the container, so mv won't work and cp has to be used), then remove the temp file.
43+
cp -f "${VHOST_DIR}/${domain}".new "${VHOST_DIR}/${domain}" && rm -f "${VHOST_DIR}/${domain}".new
44+
return 1
45+
fi
3746
}
3847

3948
function remove_all_location_configurations {
40-
local old_shopt_options=$(shopt -p) # Backup shopt options
41-
shopt -s nullglob
4249
for file in "${VHOST_DIR}"/*; do
43-
[[ -n $(sed -n "/$START_HEADER/,/$END_HEADER/p" "$file") ]] && \
44-
sed -i "/$START_HEADER/,/$END_HEADER/d" "$file"
50+
[[ -e "$file" ]] || continue
51+
if [[ -n $(sed -n "/$START_HEADER/,/$END_HEADER/p" "$file") ]]; then
52+
sed "/$START_HEADER/,/$END_HEADER/d" "$file" > "$file".new
53+
cp -f "$file".new "$file" && rm -f "$file".new
54+
fi
4555
done
46-
eval "$old_shopt_options" # Restore shopt options
4756
}
4857

4958
function check_cert_min_validity {

test/config.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ testAlias+=(
88
imageTests+=(
99
[le-companion]='
1010
docker_api
11+
location_config
1112
default_cert
1213
certs_single
1314
certs_san
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Started letsencrypt container for test location_config

test/tests/location_config/run.sh

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#!/bin/bash
2+
3+
## Test for automatic location configuration.
4+
5+
# Set variables
6+
test_comment='### This is a test comment'
7+
vhost_path='/etc/nginx/vhost.d'
8+
9+
# Create custom location configuration file to be bind mounted
10+
location_file="${TRAVIS_BUILD_DIR}/test/tests/location_config/le2.wtf"
11+
echo "$test_comment" > "$location_file"
12+
13+
# Create le1.wtf configuration file from inside the nginx container
14+
docker exec "$NGINX_CONTAINER_NAME" sh -c "echo '### This is a test comment' > /etc/nginx/vhost.d/le1.wtf"
15+
16+
# Zero the default configuration file.
17+
docker exec "$NGINX_CONTAINER_NAME" sh -c "echo '' > /etc/nginx/vhost.d/default"
18+
19+
if [[ -z $TRAVIS_CI ]]; then
20+
le_container_name="$(basename ${0%/*})_$(date "+%Y-%m-%d_%H.%M.%S")"
21+
else
22+
le_container_name="$(basename ${0%/*})"
23+
fi
24+
run_le_container "${1:?}" "$le_container_name" "--volume $location_file:$vhost_path/le2.wtf"
25+
26+
# Create the $domains array from comma separated domains in TEST_DOMAINS.
27+
IFS=',' read -r -a domains <<< "$TEST_DOMAINS"
28+
29+
# Cleanup function with EXIT trap
30+
function cleanup {
31+
# Cleanup the files created by this run of the test to avoid foiling following test(s).
32+
docker exec "$le_container_name" bash -c 'rm -rf /etc/nginx/vhost.d/le1.wtf'
33+
# Stop the LE container
34+
docker stop "$le_container_name" > /dev/null
35+
}
36+
trap cleanup EXIT
37+
38+
# Check if the ACME location configuration was correctly applied (ie only once) to the target file
39+
function check_location {
40+
local container="${1:?}"
41+
local path="${2:?}"
42+
local start_comment='## Start of configuration add by letsencrypt container'
43+
local end_comment='## End of configuration add by letsencrypt container'
44+
45+
if [[ "$(docker exec "$container" grep -c "$start_comment" "$path")" != 1 ]]; then
46+
return 1
47+
elif [[ "$(docker exec "$container" grep -c "$end_comment" "$path")" != 1 ]]; then
48+
return 1
49+
else
50+
return 0
51+
fi
52+
}
53+
54+
# default configuration file should be empty
55+
config_path="$vhost_path/default"
56+
if docker exec "$le_container_name" [ ! -s "$config_path" ]; then
57+
echo "$config_path should be empty at container startup:"
58+
docker exec "$le_container_name" cat "$config_path"
59+
fi
60+
61+
# le1.wtf and le2.wtf configuration files should only contains the test comment
62+
for domain in "${domains[@]:0:2}"; do
63+
config_path="$vhost_path/$domain"
64+
if check_location "$le_container_name" "$config_path"; then
65+
echo "Unexpected location configuration on $config_path at container startup:"
66+
docker exec "$le_container_name" cat "$config_path"
67+
elif ! docker exec "$le_container_name" grep -q "$test_comment" "$config_path"; then
68+
echo "$config_path should have test comment at container startup:"
69+
docker exec "$le_container_name" cat "$config_path"
70+
fi
71+
done
72+
73+
# le3.wtf configuration file should not exist
74+
config_path="$vhost_path/${domains[2]}"
75+
if docker exec "$le_container_name" [ -e "$config_path" ]; then
76+
echo "$config_path should not exist at container startup :"
77+
docker exec "$le_container_name" ls -lh "$config_path"
78+
docker exec "$le_container_name" cat "$config_path"
79+
fi
80+
81+
# Add default location configuration then check
82+
config_path="$vhost_path/default"
83+
docker exec "$le_container_name" bash -c 'source /app/functions.sh; add_location_configuration'
84+
if ! check_location "$le_container_name" "$config_path" ; then
85+
echo "Unexpected location configuration on $config_path after call to add_location_configuration:"
86+
docker exec "$le_container_name" cat "$config_path"
87+
fi
88+
89+
# Add le1.wtf and le2.wtf location configurations then check
90+
for domain in "${domains[@]:0:2}"; do
91+
config_path="$vhost_path/$domain"
92+
docker exec "$le_container_name" bash -c "source /app/functions.sh; add_location_configuration $domain"
93+
if ! check_location "$le_container_name" "$config_path" ; then
94+
echo "Unexpected location configuration on $config_path after call to add_location_configuration $domain:"
95+
docker exec "$le_container_name" cat "$config_path"
96+
elif ! docker exec "$le_container_name" grep -q "$test_comment" "$config_path"; then
97+
echo "$config_path should still have test comment after call to add_location_configuration $domain:"
98+
docker exec "$le_container_name" cat "$config_path"
99+
fi
100+
done
101+
102+
# Remove all location configurations
103+
docker exec "$le_container_name" bash -c "source /app/functions.sh; remove_all_location_configurations"
104+
105+
# default configuration file should be empty again
106+
config_path="$vhost_path/default"
107+
if docker exec "$le_container_name" [ ! -s "$config_path" ]; then
108+
echo "$config_path should be empty after call to remove_all_location_configurations:"
109+
docker exec "$le_container_name" cat "$config_path"
110+
fi
111+
112+
# le1.wtf and le2.wtf configuration files should have reverted to only containing the test comment
113+
for domain in "${domains[@]:0:2}"; do
114+
config_path="$vhost_path/$domain"
115+
if check_location "$le_container_name" "$config_path"; then
116+
echo "Unexpected location configuration on $config_path after call to remove_all_location_configurations:"
117+
docker exec "$le_container_name" cat "$config_path"
118+
elif ! docker exec "$le_container_name" grep -q "$test_comment" "$config_path"; then
119+
echo "$config_path should still have test comment after call to remove_all_location_configurations:"
120+
docker exec "$le_container_name" cat "$config_path"
121+
fi
122+
done
123+
124+
# Trying to add location configuration to non existing le3.wtf should only configure default
125+
docker exec "$le_container_name" bash -c "source /app/functions.sh; add_location_configuration ${domains[2]}"
126+
127+
config_path="$vhost_path/${domains[2]}"
128+
if docker exec "$le_container_name" [ -e "$config_path" ]; then
129+
echo "$config_path should not exist after call to add_location_configuration ${domains[2]}:"
130+
docker exec "$le_container_name" ls -lh "$config_path"
131+
docker exec "$le_container_name" cat "$config_path"
132+
fi
133+
134+
config_path="$vhost_path/default"
135+
docker exec "$le_container_name" bash -c 'source /app/functions.sh; add_location_configuration'
136+
if ! check_location "$le_container_name" "$config_path" ; then
137+
echo "Unexpected location configuration on $config_path after call to remove_all_location_configurations ${domains[2]}:"
138+
docker exec "$le_container_name" cat "$config_path"
139+
fi

0 commit comments

Comments
 (0)