run-docker.sh 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. #!/usr/bin/env bash
  2. # @copyright Copyright (c) 2017, Daniel Calviño Sánchez (danxuliu@gmail.com)
  3. # @copyright Copyright (c) 2018, Daniel Calviño Sánchez (danxuliu@gmail.com)
  4. #
  5. # @license GNU AGPL version 3 or any later version
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU Affero General Public License as
  9. # published by the Free Software Foundation, either version 3 of the
  10. # License, or (at your option) 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 Affero General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Affero General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. # Helper script to run the integration tests on a fresh Nextcloud server through
  20. # Docker.
  21. #
  22. # The integration tests are run in its own Docker container; the grandparent
  23. # directory of the integration tests directory (that is, the root directory of
  24. # the Nextcloud server) is copied to the container and the integration tests are
  25. # run inside it; in the container the configuration/data from the original
  26. # Nextcloud server is ignored, and a new server installation is performed inside
  27. # the container instead. Once the tests end the container is stopped.
  28. #
  29. # To perform its job, the script requires the "docker" command to be available.
  30. #
  31. # The Docker Command Line Interface (the "docker" command) requires special
  32. # permissions to talk to the Docker daemon, and those permissions are typically
  33. # available only to the root user. Please see the Docker documentation to find
  34. # out how to give access to a regular user to the Docker daemon:
  35. # https://docs.docker.com/engine/installation/linux/linux-postinstall/
  36. #
  37. # Note, however, that being able to communicate with the Docker daemon is the
  38. # same as being able to get root privileges for the system. Therefore, you must
  39. # give access to the Docker daemon (and thus run this script as) ONLY to trusted
  40. # and secure users:
  41. # https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface
  42. #
  43. # Finally, take into account that this script will automatically remove the
  44. # Docker containers named "database-nextcloud-local-test-integration" and
  45. # "nextcloud-local-test-integration", even if the script did not create them
  46. # (probably you will not have containers nor images with that name, but just in
  47. # case).
  48. # Sets the variables that abstract the differences in command names and options
  49. # between operating systems.
  50. #
  51. # Switches between mktemp on GNU/Linux and gmktemp on macOS.
  52. function setOperatingSystemAbstractionVariables() {
  53. case "$OSTYPE" in
  54. darwin*)
  55. if [ "$(which gtimeout)" == "" ]; then
  56. echo "Please install coreutils (brew install coreutils)"
  57. exit 1
  58. fi
  59. MKTEMP=gmktemp
  60. TIMEOUT=gtimeout
  61. ;;
  62. linux*)
  63. MKTEMP=mktemp
  64. TIMEOUT=timeout
  65. ;;
  66. *)
  67. echo "Operating system ($OSTYPE) not supported"
  68. exit 1
  69. ;;
  70. esac
  71. }
  72. # Launches the database server in a Docker container.
  73. #
  74. # No server is started if "SQLite" is being used; in other cases the database
  75. # is set up as needed and generic "$DATABASE_NAME/USER/PASSWORD" variables
  76. # (independent of the database type) are set to be used when installing the
  77. # Nextcloud server.
  78. #
  79. # The Docker container started here will be automatically stopped when the
  80. # script exits (see cleanUp). If the database server can not be started then the
  81. # script will be exited immediately with an error state.
  82. function prepareDatabase() {
  83. if [ "$DATABASE" = "sqlite" ]; then
  84. return
  85. fi
  86. DATABASE_CONTAINER=database-nextcloud-local-test-integration
  87. DATABASE_NAME=oc_autotest
  88. DATABASE_USER=oc_autotest
  89. DATABASE_PASSWORD=nextcloud
  90. DATABASE_CONTAINER_OPTIONS="--env MYSQL_ROOT_PASSWORD=nextcloud_root --env MYSQL_USER=$DATABASE_USER --env MYSQL_PASSWORD=$DATABASE_PASSWORD --env MYSQL_DATABASE=$DATABASE_NAME"
  91. if [ "$DATABASE" = "pgsql" ]; then
  92. DATABASE_CONTAINER_OPTIONS=" --env POSTGRES_USER=$DATABASE_USER --env POSTGRES_PASSWORD=$DATABASE_PASSWORD --env POSTGRES_DB=${DATABASE_NAME}_dummy"
  93. fi
  94. echo "Starting database server"
  95. docker run --detach --name=$DATABASE_CONTAINER $DATABASE_CONTAINER_OPTIONS $DATABASE_IMAGE
  96. DATABASE_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $DATABASE_CONTAINER)
  97. DATABASE_PORT=3306
  98. if [ "$DATABASE" = "pgsql" ]; then
  99. DATABASE_PORT=5432
  100. fi
  101. echo "Waiting for database server to be ready"
  102. if ! $TIMEOUT 600s bash -c "while ! (</dev/tcp/$DATABASE_IP/$DATABASE_PORT) >/dev/null 2>&1; do sleep 1; done"; then
  103. echo "Could not start database server after 600 seconds" >&2
  104. exit 1
  105. fi
  106. }
  107. # Creates a Docker container to run the integration tests.
  108. #
  109. # This function starts a Docker container with a copy of the Nextcloud code from
  110. # the grandparent directory, although ignoring any configuration or data that it
  111. # may provide (for example, if that directory was used directly to deploy a
  112. # Nextcloud instance in a web server). As the Nextcloud code is copied to the
  113. # container instead of referenced the original code can be modified while the
  114. # integration tests are running without interfering in them.
  115. function prepareDocker() {
  116. NEXTCLOUD_LOCAL_CONTAINER=nextcloud-local-test-integration
  117. NEXTCLOUD_LOCAL_CONTAINER_NETWORK_OPTIONS=""
  118. if [ -n "$DATABASE_CONTAINER" ]; then
  119. # The network stack is shared between the database and the Nextcloud
  120. # container, so the Nextcloud server can access the database directly on
  121. # 127.0.0.1.
  122. NEXTCLOUD_LOCAL_CONTAINER_NETWORK_OPTIONS="--network=container:$DATABASE_CONTAINER"
  123. fi
  124. echo "Starting the Nextcloud container"
  125. # When using "nextcloudci/phpX.Y" images the container exits immediately if
  126. # no command is given, so a Bash session is created to prevent that.
  127. docker run \
  128. --volume composer_cache:/root/.composer \
  129. --detach --name=$NEXTCLOUD_LOCAL_CONTAINER $NEXTCLOUD_LOCAL_CONTAINER_NETWORK_OPTIONS --interactive --tty $NEXTCLOUD_LOCAL_IMAGE bash
  130. # Use the $TMPDIR or, if not set, fall back to /tmp.
  131. NEXTCLOUD_LOCAL_TAR="$($MKTEMP --tmpdir="${TMPDIR:-/tmp}" --suffix=.tar nextcloud-local-XXXXXXXXXX)"
  132. # Setting the user and group of files in the tar would be superfluous, as
  133. # "docker cp" does not take them into account (the extracted files are set
  134. # to root).
  135. echo "Copying local Git working directory of Nextcloud to the container"
  136. tar --create --file="$NEXTCLOUD_LOCAL_TAR" \
  137. --exclude=".git" \
  138. --exclude="./config/config.php" \
  139. --exclude="./data" \
  140. --exclude="./data-autotest" \
  141. --exclude="./tests" \
  142. --exclude="node_modules" \
  143. --directory=../../ \
  144. .
  145. docker exec $NEXTCLOUD_LOCAL_CONTAINER mkdir /nextcloud
  146. docker cp - $NEXTCLOUD_LOCAL_CONTAINER:/nextcloud/ < "$NEXTCLOUD_LOCAL_TAR"
  147. # Database options are needed only when a database other than SQLite is
  148. # used.
  149. NEXTCLOUD_LOCAL_CONTAINER_INSTALL_DATABASE_OPTIONS=""
  150. if [ -n "$DATABASE_CONTAINER" ]; then
  151. NEXTCLOUD_LOCAL_CONTAINER_INSTALL_DATABASE_OPTIONS="--database=$DATABASE --database-name=$DATABASE_NAME --database-user=$DATABASE_USER --database-pass=$DATABASE_PASSWORD --database-host=127.0.0.1"
  152. fi
  153. echo "Installing Nextcloud in the container"
  154. docker exec $NEXTCLOUD_LOCAL_CONTAINER bash -c "cd nextcloud && php occ maintenance:install --admin-pass=admin $NEXTCLOUD_LOCAL_CONTAINER_INSTALL_DATABASE_OPTIONS"
  155. }
  156. # Removes/stops temporal elements created/started by this script.
  157. function cleanUp() {
  158. # Disable (yes, "+" disables) exiting immediately on errors to ensure that
  159. # all the cleanup commands are executed (well, no errors should occur during
  160. # the cleanup anyway, but just in case).
  161. set +o errexit
  162. echo "Cleaning up"
  163. if [ -f "$NEXTCLOUD_LOCAL_TAR" ]; then
  164. echo "Removing $NEXTCLOUD_LOCAL_TAR"
  165. rm $NEXTCLOUD_LOCAL_TAR
  166. fi
  167. # The name filter must be specified as "^/XXX$" to get an exact match; using
  168. # just "XXX" would match every name that contained "XXX".
  169. if [ -n "$(docker ps --all --quiet --filter name="^/$NEXTCLOUD_LOCAL_CONTAINER$")" ]; then
  170. echo "Removing Docker container $NEXTCLOUD_LOCAL_CONTAINER"
  171. docker rm --volumes --force $NEXTCLOUD_LOCAL_CONTAINER
  172. fi
  173. if [ -n "$DATABASE_CONTAINER" -a -n "$(docker ps --all --quiet --filter name="^/$DATABASE_CONTAINER$")" ]; then
  174. echo "Removing Docker container $DATABASE_CONTAINER"
  175. docker rm --volumes --force $DATABASE_CONTAINER
  176. fi
  177. }
  178. # Exit immediately on errors.
  179. set -o errexit
  180. # Execute cleanUp when the script exits, either normally or due to an error.
  181. trap cleanUp EXIT
  182. # Ensure working directory is script directory, as some actions (like copying
  183. # the Git working directory to the container) expect that.
  184. cd "$(dirname $0)"
  185. # "--image XXX" option can be provided to set the Docker image to use to run
  186. # the integration tests (one of the "nextcloudci/phpX.Y:phpX.Y-Z" images).
  187. NEXTCLOUD_LOCAL_IMAGE="nextcloudci/php7.3:php7.3-5"
  188. if [ "$1" = "--image" ]; then
  189. NEXTCLOUD_LOCAL_IMAGE=$2
  190. shift 2
  191. fi
  192. # "--database XXX" option can be provided to set the database to use to run the
  193. # integration tests (one of "sqlite", "mysql" or "pgsql"; "sqlite" is used
  194. # by default).
  195. DATABASE="sqlite"
  196. if [ "$1" = "--database" ]; then
  197. DATABASE=$2
  198. shift 2
  199. fi
  200. if [ "$DATABASE" != "sqlite" ] && [ "$DATABASE" != "mysql" ] && [ "$DATABASE" != "pgsql" ]; then
  201. echo "--database must be followed by one of: sqlite, mysql or pgsql"
  202. exit 1
  203. fi
  204. # "--database-image XXX" option can be provided to set the Docker image to use
  205. # for the database container (ignored when using "sqlite").
  206. if [ "$DATABASE" = "mysql" ]; then
  207. DATABASE_IMAGE="mysql:5.7"
  208. elif [ "$DATABASE" = "pgsql" ]; then
  209. DATABASE_IMAGE="postgres:10"
  210. fi
  211. if [ "$1" = "--database-image" ]; then
  212. DATABASE_IMAGE=$2
  213. shift 2
  214. fi
  215. # If no parameter is provided to this script all the integration tests are run.
  216. SCENARIO_TO_RUN=$1
  217. setOperatingSystemAbstractionVariables
  218. prepareDatabase
  219. prepareDocker
  220. echo "Running tests"
  221. # --tty is needed to get colourful output.
  222. docker exec --tty $NEXTCLOUD_LOCAL_CONTAINER bash -c "cd nextcloud/build/integration && ./run.sh $SCENARIO_TO_RUN"