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