]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/docker-test-helper.sh
import ceph quincy 17.2.6
[ceph.git] / ceph / src / test / docker-test-helper.sh
1 #!/usr/bin/env bash
2 #
3 # Copyright (C) 2014, 2015 Red Hat <contact@redhat.com>
4 #
5 # Author: Loic Dachary <loic@dachary.org>
6 #
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU Library Public License as published by
9 # the Free Software Foundation; either version 2, or (at your option)
10 # any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Library Public License for more details.
16 #
17 function get_image_name() {
18 local os_type=$1
19 local os_version=$2
20
21 echo ceph-$os_type-$os_version-$USER
22 }
23
24 function setup_container() {
25 local os_type=$1
26 local os_version=$2
27 local dockercmd=$3
28 local opts="$4"
29
30 # rm not valid here
31 opts=${opts//' --rm'};
32
33 local image=$(get_image_name $os_type $os_version)
34 local build=true
35 if $dockercmd images $image | grep --quiet "^$image " ; then
36 eval touch --date=$($dockercmd inspect $image | jq '.[0].Created') $image
37 found=$(find -L test/$os_type-$os_version/* -newer $image)
38 rm $image
39 if test -n "$found" ; then
40 $dockercmd rmi $image
41 else
42 build=false
43 fi
44 fi
45 if $build ; then
46 #
47 # In the dockerfile,
48 # replace environment variables %%FOO%% with their content
49 #
50 rm -fr dockerfile
51 cp --dereference --recursive test/$os_type-$os_version dockerfile
52 os_version=$os_version user_id=$(id -u) \
53 perl -p -e 's/%%(\w+)%%/$ENV{$1}/g' \
54 dockerfile/Dockerfile.in > dockerfile/Dockerfile
55 $dockercmd $opts build --tag=$image dockerfile
56 rm -fr dockerfile
57 fi
58 }
59
60 function get_upstream() {
61 git rev-parse --show-toplevel
62 }
63
64 function get_downstream() {
65 local os_type=$1
66 local os_version=$2
67
68 local image=$(get_image_name $os_type $os_version)
69 local upstream=$(get_upstream)
70 local dir=$(dirname $upstream)
71 echo "$dir/$image"
72 }
73
74 function setup_downstream() {
75 local os_type=$1
76 local os_version=$2
77 local ref=$3
78
79 local image=$(get_image_name $os_type $os_version)
80 local upstream=$(get_upstream)
81 local dir=$(dirname $upstream)
82 local downstream=$(get_downstream $os_type $os_version)
83
84 (
85 cd $dir
86 if ! test -d $downstream ; then
87 # Inspired by https://github.com/git/git/blob/master/contrib/workdir/git-new-workdir
88 mkdir -p $downstream/.git || return 1
89 for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache
90 do
91 case $x in
92 */*)
93 mkdir -p "$downstream/.git/$x"
94 ;;
95 esac
96 ln -s "$upstream/.git/$x" "$downstream/.git/$x"
97 done
98 cp "$upstream/.git/HEAD" "$downstream/.git/HEAD"
99 fi
100 cd $downstream
101 git reset --hard $ref || return 1
102 git submodule sync --recursive || return 1
103 git submodule update --force --init --recursive || return 1
104 )
105 }
106
107 function run_in_docker() {
108 local os_type=$1
109 shift
110 local os_version=$1
111 shift
112 local ref=$1
113 shift
114 local dockercmd=$1
115 shift
116 local opts="$1"
117 shift
118 local script=$1
119
120 setup_downstream $os_type $os_version $ref || return 1
121 setup_container $os_type $os_version $dockercmd "$opts" || return 1
122 local downstream=$(get_downstream $os_type $os_version)
123 local image=$(get_image_name $os_type $os_version)
124 local upstream=$(get_upstream)
125 local ccache
126 mkdir -p $HOME/.ccache
127 ccache="--volume $HOME/.ccache:$HOME/.ccache"
128 user="--user $USER"
129 local cmd="$dockercmd run $opts --name $image --privileged $ccache"
130 cmd+=" --volume $downstream:$downstream"
131 cmd+=" --volume $upstream:$upstream"
132 if test "$dockercmd" = "podman" ; then
133 cmd+=" --userns=keep-id"
134 fi
135 local status=0
136 if test "$script" = "SHELL" ; then
137 echo Running: $cmd --tty --interactive --workdir $downstream $user $image bash
138 $cmd --tty --interactive --workdir $downstream $user $image bash
139 else
140 echo Running: $cmd --workdir $downstream $user $image "$@"
141 if ! $cmd --workdir $downstream $user $image "$@" ; then
142 status=1
143 fi
144 fi
145 return $status
146 }
147
148 function remove_all() {
149 local os_type=$1
150 local os_version=$2
151 local dockercmd=$3
152 local image=$(get_image_name $os_type $os_version)
153
154 $dockercmd rm $image
155 $dockercmd rmi $image
156 }
157
158 function usage() {
159 cat <<EOF
160 Run commands within Ceph sources, in a container. Use podman if available,
161 docker if not.
162 $0 [options] command args ...
163
164 [-h|--help] display usage
165 [--verbose] trace all shell lines
166
167 [--os-type type] docker image repository (centos, ubuntu, etc.)
168 (defaults to ubuntu)
169 [--os-version version] docker image tag (7 for centos, 16.04 for ubuntu, etc.)
170 (defaults to 16.04)
171 [--ref gitref] git reset --hard gitref before running the command
172 (defaults to git rev-parse HEAD)
173 [--all types+versions] list of docker image repositories and tags
174
175 [--shell] run an interactive shell in the container
176 [--remove-all] remove the container and the image for the specified types+versions
177 [--no-rm] don't remove the container when finished
178
179 [--opts options] run the container with 'options'
180
181 docker-test.sh must be run from a Ceph clone and it will run the
182 command in a container, using a copy of the clone so that long running
183 commands such as make check are not disturbed while development
184 continues. Here is a sample use case including an interactive session
185 and running a unit test:
186
187 $ grep PRETTY_NAME /etc/os-release
188 PRETTY_NAME="Ubuntu 16.04.7 LTS"
189 $ test/docker-test.sh --os-type centos --os-version 7 --shell
190 HEAD is now at 1caee81 autotools: add --enable-docker
191 bash-4.2$ pwd
192 /srv/ceph/ceph-centos-7
193 bash-4.2$ cat /etc/redhat-release
194 CentOS Linux release 7.6.1810 (Core)
195 bash-4.2$
196 $ time test/docker-test.sh --os-type centos --os-version 7 unittest_str_map
197 HEAD is now at 1caee81 autotools: add --enable-docker
198 Running main() from gtest_main.cc
199 [==========] Running 2 tests from 1 test case.
200 [----------] Global test environment set-up.
201 [----------] 2 tests from str_map
202 [ RUN ] str_map.json
203 [ OK ] str_map.json (1 ms)
204 [ RUN ] str_map.plaintext
205 [ OK ] str_map.plaintext (0 ms)
206 [----------] 2 tests from str_map (1 ms total)
207
208 [----------] Global test environment tear-down
209 [==========] 2 tests from 1 test case ran. (1 ms total)
210 [ PASSED ] 2 tests.
211
212 real 0m3.759s
213 user 0m0.074s
214 sys 0m0.051s
215
216 The --all argument is a bash associative array literal listing the
217 operating system version for each operating system type. For instance
218
219 docker-test.sh --all '([ubuntu]="16.04 17.04" [centos]="7")'
220
221 is strictly equivalent to
222
223 docker-test.sh --os-type ubuntu --os-version 16.04
224 docker-test.sh --os-type ubuntu --os-version 17.04
225 docker-test.sh --os-type centos --os-version 7
226
227 The --os-type and --os-version must be exactly as displayed by docker images:
228
229 $ docker images
230 REPOSITORY TAG IMAGE ID ...
231 centos 7 87e5b6b3ccc1 ...
232 ubuntu 16.04 6b4e8a7373fe ...
233
234 The --os-type value can be any string in the REPOSITORY column, the --os-version
235 can be any string in the TAG column.
236
237 The --shell and --remove actions are mutually exclusive.
238
239 Run make check in centos 7
240 docker-test.sh --os-type centos --os-version 7 -- make check
241
242 Run make check on a giant
243 docker-test.sh --ref giant -- make check
244
245 Run an interactive shell and set resolv.conf to use 172.17.42.1
246 docker-test.sh --opts --dns=172.17.42.1 --shell
247
248 Run make check on centos 7, ubuntu 16.04 and ubuntu 17.04
249 docker-test.sh --all '([ubuntu]="16.04 17.04" [centos]="7")' -- make check
250 EOF
251 }
252
253 function main_docker() {
254 local dockercmd="docker"
255 if type podman > /dev/null; then
256 dockercmd="podman"
257 fi
258
259 if ! $dockercmd ps > /dev/null 2>&1 ; then
260 echo "docker not available: $0"
261 return 0
262 fi
263
264 local temp
265 temp=$(getopt -o scht:v:o:a:r: --long remove-all,verbose,shell,no-rm,help,os-type:,os-version:,opts:,all:,ref: -n $0 -- "$@") || return 1
266
267 eval set -- "$temp"
268
269 local os_type=ubuntu
270 local os_version=16.04
271 local all
272 local remove=false
273 local shell=false
274 local opts
275 local ref=$(git rev-parse HEAD)
276 local no-rm=false
277
278 while true ; do
279 case "$1" in
280 --remove-all)
281 remove=true
282 shift
283 ;;
284 --verbose)
285 set -xe
286 PS4='${BASH_SOURCE[0]}:$LINENO: ${FUNCNAME[0]}: '
287 shift
288 ;;
289 -s|--shell)
290 shell=true
291 shift
292 ;;
293 -h|--help)
294 usage
295 return 0
296 ;;
297 -t|--os-type)
298 os_type=$2
299 shift 2
300 ;;
301 -v|--os-version)
302 os_version=$2
303 shift 2
304 ;;
305 -o|--opts)
306 opts="$2"
307 shift 2
308 ;;
309 -a|--all)
310 all="$2"
311 shift 2
312 ;;
313 -r|--ref)
314 ref="$2"
315 shift 2
316 ;;
317 --no-rm)
318 no-rm=true
319 shift
320 ;;
321 --)
322 shift
323 break
324 ;;
325 *)
326 echo "unexpected argument $1"
327 return 1
328 ;;
329 esac
330 done
331
332 if test -z "$all" ; then
333 all="([$os_type]=\"$os_version\")"
334 fi
335
336 declare -A os_type2versions
337 eval os_type2versions="$all"
338
339 if ! $no-rm ; then
340 opts+=" --rm"
341 fi
342
343 for os_type in ${!os_type2versions[@]} ; do
344 for os_version in ${os_type2versions[$os_type]} ; do
345 if $remove ; then
346 remove_all $os_type $os_version $dockercmd || return 1
347 elif $shell ; then
348 run_in_docker $os_type $os_version $ref $dockercmd "$opts" SHELL || return 1
349 else
350 run_in_docker $os_type $os_version $ref $dockercmd "$opts" "$@" || return 1
351 fi
352 done
353 done
354 }