Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / test / docker-test-helper.sh
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 }