| #!/bin/bash |
| # Copyright 2016 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| set -e |
| |
| # Utility functions |
| DOCKER_VERSION="1.10.3" |
| TEMP_OBJECTS=() |
| |
| on_exit() { |
| # clear all temp objects |
| for t in "${TEMP_OBJECTS[@]}"; do |
| echo "Removing temp object ${t}" |
| rm -rf "${t}" |
| done |
| TEMP_OBJECTS=() |
| } |
| trap on_exit EXIT |
| |
| die() { |
| echo "ERROR: $*" |
| exit 1 |
| } |
| |
| warn() { |
| echo "WARNING: $*" |
| } |
| |
| is_macosx() { |
| [ "$(uname -s)" = "Darwin" ] |
| } |
| |
| realpath() { |
| # Try to find "realpath" in a portable way. |
| if type python >/dev/null 2>&1; then |
| python -c 'import sys; import os; print os.path.realpath(sys.argv[1])' "$1" |
| else |
| readlink -f "$@" |
| fi |
| } |
| |
| check_docker() { |
| if ! type docker >/dev/null 2>&1; then |
| die "Docker not installed, abort." |
| fi |
| DOCKER="docker" |
| if [ "$(id -un)" != "root" ]; then |
| if ! echo "begin $(id -Gn) end" | grep -q " docker "; then |
| echo "You are neither root nor in the docker group," |
| echo "so you'll be asked for root permission..." |
| DOCKER="sudo docker" |
| fi |
| fi |
| |
| # Check Docker version |
| local docker_version="$(${DOCKER} version --format '{{.Server.Version}}' \ |
| 2>/dev/null)" |
| if [ -z "${docker_version}" ]; then |
| # Old Docker (i.e., 1.6.2) does not support --format. |
| docker_version="$(${DOCKER} version | sed -n 's/Server version: //p')" |
| fi |
| local error_message="Require Docker version >= ${DOCKER_VERSION} but you have ${docker_version}" |
| local required_version=(${DOCKER_VERSION//./ }) |
| local current_version=(${docker_version//./ }) |
| for ((i = 0; i < ${#required_version[@]}; ++i)); do |
| if (( ${#current_version[@]} <= i )); then |
| die "${error_message}" # the current version array is not long enough |
| elif (( ${required_version[$i]} < ${current_version[$i]} )); then |
| break |
| elif (( ${required_version[$i]} > ${current_version[$i]} )); then |
| die "${error_message}" |
| fi |
| done |
| } |
| |
| check_gsutil() { |
| if ! type gsutil >/dev/null 2>&1; then |
| die "Cannot find gsutil, please install gsutil first" |
| fi |
| } |
| |
| check_xz() { |
| XZ="pixz" |
| if ! type pixz >/dev/null 2>&1; then |
| warn "pixz is not installed, fall back to xz, compression will be slow!!!" |
| warn "Install pixz if you don't want to wait for too long" |
| XZ="xz" |
| fi |
| } |
| |
| upload_to_localmirror() { |
| local local_file_path="$1" |
| local remote_file_url="$2" |
| |
| echo "Uploading to chromeos-localmirror" |
| gsutil cp "${local_file_path}" "${remote_file_url}" |
| gsutil acl ch -u AllUsers:R "${remote_file_url}" |
| } |
| |
| run_in_factory() { |
| (cd "${FACTORY_DIR}"; "$@") |
| } |
| |
| # Base directories |
| SCRIPT_DIR="$(dirname "$(realpath "$0")")" |
| FACTORY_DIR="$(dirname "${SCRIPT_DIR}")" |
| UMPIRE_DIR="${FACTORY_DIR}/py/umpire" |
| DOME_DIR="${FACTORY_DIR}/py/dome" |
| OVERLORD_DIR="${FACTORY_DIR}/go/src/overlord" |
| BUILD_DIR="${FACTORY_DIR}/build/docker" |
| |
| # Platform specific directory defaults. |
| if is_macosx; then |
| DEFAULT_HOST_SHARED_DIR="${HOME}/cros_docker" |
| # osxfs needs the folder to be owned by current user. |
| SHARED_WITH_USER_ACL=true |
| # The /etc/localtime is not available on recent Docker builds. |
| DEFAULT_HOST_LOCALTIME_PATH="" |
| else |
| DEFAULT_HOST_SHARED_DIR="/cros_docker" |
| SHARED_WITH_USER_ACL=false |
| DEFAULT_HOST_LOCALTIME_PATH=/etc/localtime |
| fi |
| |
| # Directories on host that would be mounted to docker |
| # This would be overridden in integration tests. |
| : "${HOST_SHARED_DIR:="${DEFAULT_HOST_SHARED_DIR}"}" |
| : "${HOST_LOCALTIME_PATH:="${DEFAULT_HOST_LOCALTIME_PATH}"}" |
| HOST_DOME_DIR="${HOST_SHARED_DIR}/dome" |
| HOST_TFTP_DIR="${HOST_SHARED_DIR}/tftp" |
| HOST_UMPIRE_DIR="${HOST_SHARED_DIR}/umpire" |
| HOST_OVERLORD_DIR="${HOST_SHARED_DIR}/overlord" |
| HOST_OVERLORD_CONFIG_DIR="${HOST_OVERLORD_DIR}/config" |
| HOST_GOOFY_DIR="${HOST_SHARED_DIR}/goofy" |
| |
| # Shared temporary volume between Dome and Umpire. |
| HOST_SHARED_TMP_VOLUME="cros-docker-shared-tmp-vol" |
| |
| # Publish tools |
| PREBUILT_IMAGE_SITE="https://storage.googleapis.com" |
| PREBUILT_IMAGE_DIR_URL="${PREBUILT_IMAGE_SITE}/chromeos-localmirror/distfiles" |
| GSUTIL_BUCKET="gs://chromeos-localmirror/distfiles" |
| COMMIT_SUBJECT="setup: Publish cros_docker image version" |
| |
| # Remote resources |
| RESOURCE_DOCKER_URL="${PREBUILT_IMAGE_DIR_URL}/docker-${DOCKER_VERSION}.tgz" |
| RESOURCE_DOCKER_SHA1="17239c2d84413affa68bbe444c3402905e863d1f" |
| RESOURCE_CROS_DOCKER_URL="https://chromium.googlesource.com/chromiumos/platform/factory/+/master/setup/cros_docker.sh?format=TEXT" |
| RESOURCE_PIXZ_URL="${PREBUILT_IMAGE_DIR_URL}/pixz-1.0.6-amd64-static.tbz2" |
| RESOURCE_PIXZ_SHA1="3bdf7473df19f2d089f2a9b055c18a4f7f1409e5" |
| |
| # Directories inside docker |
| DOCKER_BASE_DIR="/usr/local/factory" |
| DOCKER_DOME_DIR="${DOCKER_BASE_DIR}/py/dome" |
| DOCKER_DOME_FRONTEND_DIR="${DOCKER_DOME_DIR}/frontend" |
| DOCKER_UMPIRE_DIR="${DOCKER_BASE_DIR}/py/umpire" |
| DOCKER_INSTALOG_DIR="${DOCKER_BASE_DIR}/py/instalog" |
| |
| DOCKER_OVERLORD_DIR="${DOCKER_BASE_DIR}/bin/overlord" |
| DOCKER_OVERLORD_CONFIG_DIR="${DOCKER_OVERLORD_DIR}/config" |
| |
| DOCKER_SHARED_TMP_DIR="/tmp/shared" |
| |
| # Umpire's db directory mount point in Dome |
| DOCKER_UMPIRE_DIR_IN_DOME="/var/db/factory/umpire" |
| |
| # TFTP root mount point in Dome |
| DOCKER_TFTP_DIR_IN_DOME="/var/tftp" |
| |
| # DOCKER_IMAGE_{GITHASH,TIMESTAMP} will be updated when you publish. |
| DOCKER_IMAGE_GITHASH="a98e223d33352c246cbe7d25230a2b752704900e" |
| DOCKER_IMAGE_TIMESTAMP="20190313141545" |
| DOCKER_IMAGE_NAME="cros/factory_server" |
| |
| if [ -n "${HOST_LOCALTIME_PATH}" ]; then |
| DOCKER_LOCALTIME_VOLUME="--volume ${HOST_LOCALTIME_PATH}:/etc/localtime:ro" |
| else |
| DOCKER_LOCALTIME_VOLUME="" |
| fi |
| |
| # Configures docker image file information by DOCKER_IMAGE_{GITHASH,TIMESTAMP}. |
| set_docker_image_info() { |
| DOCKER_IMAGE_BUILD="${DOCKER_IMAGE_TIMESTAMP}-${DOCKER_IMAGE_GITHASH:0:6}" |
| DOCKER_IMAGE_VERSION="${DOCKER_IMAGE_BUILD}-docker-${DOCKER_VERSION}" |
| DOCKER_IMAGE_FILENAME="factory-server-${DOCKER_IMAGE_VERSION}.txz" |
| DOCKER_IMAGE_FILEPATH="${SCRIPT_DIR}/${DOCKER_IMAGE_FILENAME}" |
| } |
| # Set DOCKER_IMAGE_* variables immediately. |
| set_docker_image_info |
| |
| # Things that can be override by environment variable |
| : "${PROJECT:="$(cat "${HOST_UMPIRE_DIR}/.default_project" 2>/dev/null)"}" |
| : "${PROJECT:="default"}" |
| : "${UMPIRE_CONTAINER_NAME:="umpire_${PROJECT}"}" |
| : "${UMPIRE_CONTAINER_DIR:="${HOST_UMPIRE_DIR}/${PROJECT}"}" |
| : "${UMPIRE_PORT:="8080"}" # base port for Umpire |
| : "${DOME_PORT:="8000"}" # port to access Dome |
| : "${DOME_DEV_PORT:="18000"}" # port to access Dome dev server |
| : "${GOOFY_PORT:="4012"}" # port to access Goofy |
| : "${OVERLORD_HTTP_PORT:="9000"}" # port to access Overlord |
| : "${OVERLORD_LAN_DISC_IFACE:=""}" # The network interface that Overlord LAN |
| # discovery should be run on. |
| |
| DOME_UWSGI_CONTAINER_NAME="dome_uwsgi" |
| DOME_NGINX_CONTAINER_NAME="dome_nginx" |
| |
| DOME_BUILDER_IMAGE_NAME="cros/dome-builder" |
| |
| DOME_DEV_FRONTEND_CONTAINER_NAME="dome_dev_frontend" |
| DOME_DEV_DJANGO_CONTAINER_NAME="dome_dev_django" |
| DOME_DEV_NGINX_CONTAINER_NAME="dome_dev_nginx" |
| DOME_DEV_DOCKER_NETWORK_NAME="dome_dev_network" |
| |
| ensure_dir() { |
| local dir="$1" |
| if [ ! -d "${dir}" ]; then |
| sudo mkdir -p "${dir}" |
| fi |
| } |
| |
| ensure_dir_acl() { |
| local dir="$1" |
| if "${SHARED_WITH_USER_ACL}"; then |
| sudo chown -R "$(id -u)" "${dir}" |
| sudo chgrp -R "$(id -g)" "${dir}" |
| fi |
| } |
| |
| check_file_sha1() { |
| local file="$1" |
| local sha1="$2" |
| [[ -f "${file}" ]] && echo "${sha1} ${file}" | sha1sum -c |
| } |
| |
| fetch_resource() { |
| local local_name="$1" |
| local url="$2" |
| local sha1="$3" |
| |
| if ! check_file_sha1 "${local_name}" "${sha1}"; then |
| rm -f "${local_name}" |
| curl -L --fail "${url}" -o "${local_name}" || rm -f "${local_name}" |
| if ! check_file_sha1 "${local_name}" "${sha1}"; then |
| die "Error when fetching resource ${url}" |
| fi |
| fi |
| } |
| |
| check_git_status() { |
| [ -z "$(run_in_factory git status --porcelain)" ] |
| } |
| |
| get_git_hash() { |
| run_in_factory git rev-parse HEAD |
| } |
| |
| stop_and_remove_container() { |
| ${DOCKER} stop "$1" 2>/dev/null || true |
| ${DOCKER} rm "$1" 2>/dev/null || true |
| } |
| |
| # Section for Umpire subcommand |
| do_umpire_run() { |
| check_docker |
| |
| # Separate umpire db for each container. |
| local docker_db_dir="/var/db/factory/umpire" |
| |
| ensure_dir "${HOST_SHARED_DIR}" |
| ensure_dir "${UMPIRE_CONTAINER_DIR}" |
| ensure_dir_acl "${HOST_SHARED_DIR}" |
| |
| stop_and_remove_container "${UMPIRE_CONTAINER_NAME}" |
| |
| echo "Starting Umpire container ..." |
| |
| local p1=${UMPIRE_PORT} # Imaging & Shopfloor |
| local p2=$((UMPIRE_PORT + 2)) # CLI RPC |
| local p3=$((UMPIRE_PORT + 4)) # Rsync |
| local p4=$((UMPIRE_PORT + 6)) # Instalog output_pull_socket plugin |
| local p5=$((UMPIRE_PORT + 8)) # Instalog customized output plugin |
| |
| local umpire_base_port=8080 |
| local umpire_cli_port=$((umpire_base_port + 2)) |
| local umpire_rsync_port=$((umpire_base_port + 4)) |
| local umpire_instalog_pull_socket_port=$((umpire_base_port + 6)) |
| local umpire_instalog_customized_output_port=$((umpire_base_port + 8)) |
| |
| ${DOCKER} run \ |
| --detach \ |
| --restart unless-stopped \ |
| --name "${UMPIRE_CONTAINER_NAME}" \ |
| --tmpfs "/run:rw,size=16384k" \ |
| ${DOCKER_LOCALTIME_VOLUME} \ |
| --volume "${HOST_SHARED_DIR}:/mnt" \ |
| --volume "${UMPIRE_CONTAINER_DIR}:${docker_db_dir}" \ |
| --volume "${HOST_SHARED_TMP_VOLUME}:${DOCKER_SHARED_TMP_DIR}" \ |
| --publish "${p1}:${umpire_base_port}" \ |
| --publish "${p2}:${umpire_cli_port}" \ |
| --publish "${p3}:${umpire_rsync_port}" \ |
| --publish "${p4}:${umpire_instalog_pull_socket_port}" \ |
| --publish "${p5}:${umpire_instalog_customized_output_port}" \ |
| --env "UMPIRE_PROJECT_NAME=${PROJECT}" \ |
| --privileged \ |
| "${DOCKER_IMAGE_NAME}" \ |
| "${DOCKER_BASE_DIR}/bin/umpired" || \ |
| (echo "Removing stale container due to error ..."; \ |
| ${DOCKER} rm "${UMPIRE_CONTAINER_NAME}"; \ |
| die "Can't start umpire docker. Possibly wrong port binding?") |
| |
| echo "done" |
| echo |
| echo "*** NOTE ***" |
| echo "- Host directory ${HOST_SHARED_DIR} is mounted" \ |
| "under /mnt in the container." |
| echo "- Host directory ${UMPIRE_CONTAINER_DIR} is mounted" \ |
| "under ${docker_db_dir} in the container." |
| echo "- Umpire service ports is mapped to the local machine." |
| } |
| |
| do_umpire_destroy() { |
| check_docker |
| |
| echo -n "Deleting ${UMPIRE_CONTAINER_NAME} container ... " |
| ${DOCKER} stop "${UMPIRE_CONTAINER_NAME}" >/dev/null 2>&1 || true |
| ${DOCKER} rm "${UMPIRE_CONTAINER_NAME}" >/dev/null 2>&1 || true |
| echo "done" |
| } |
| |
| check_container_status() { |
| local container_name="$1" |
| if ! ${DOCKER} ps --format "{{.Names}} {{.Status}}" \ |
| | grep -q "${container_name} Up "; then |
| die "${container_name} container is not running" |
| fi |
| } |
| |
| do_umpire_shell() { |
| check_docker |
| check_container_status "${UMPIRE_CONTAINER_NAME}" |
| |
| ${DOCKER} exec -it "${UMPIRE_CONTAINER_NAME}" sh |
| } |
| |
| do_umpire_test() { |
| check_docker |
| |
| do_build |
| |
| local umpire_tester_image_name="cros/umpire_tester" |
| local dockerfile="${UMPIRE_DIR}/server/e2e_test/Dockerfile" |
| |
| fetch_resource "${BUILD_DIR}/docker.tgz" \ |
| "${RESOURCE_DOCKER_URL}" "${RESOURCE_DOCKER_SHA1}" |
| |
| ${DOCKER} build \ |
| --file "${dockerfile}" \ |
| --tag "${umpire_tester_image_name}" \ |
| --build-arg server_dir="${DOCKER_BASE_DIR}" \ |
| --build-arg umpire_dir="${DOCKER_UMPIRE_DIR}" \ |
| "${FACTORY_DIR}" |
| |
| local temp_dir="$(mktemp -d)" |
| TEMP_OBJECTS=("${temp_dir}" "${TEMP_OBJECTS[@]}") |
| |
| ${DOCKER} run \ |
| --rm \ |
| --net=host \ |
| ${DOCKER_LOCALTIME_VOLUME} \ |
| --volume "${temp_dir}:${temp_dir}" \ |
| --volume /run/docker.sock:/run/docker.sock \ |
| --volume /run \ |
| --env "TMPDIR=${temp_dir}" \ |
| --env "LOG_LEVEL=${LOG_LEVEL}" \ |
| "${umpire_tester_image_name}" \ |
| "${DOCKER_UMPIRE_DIR}/server/e2e_test/e2e_test.py" "$@" |
| |
| touch "${UMPIRE_DIR}/.tests-passed" |
| } |
| |
| umpire_usage() { |
| cat << __EOF__ |
| Umpire: the Unified Factory Server deployment script |
| |
| You can change target Umpire container (default ${UMPIRE_CONTAINER_NAME}) by |
| assigning the UMPIRE_CONTAINER_NAME environment variable. |
| |
| commands: |
| $0 umpire help |
| Show this help message. |
| |
| $0 umpire run |
| Create and start Umpire containers. |
| |
| You can change the umpire base port (default ${UMPIRE_PORT}) by assigning |
| the UMPIRE_PORT environment variable. |
| Umpire would bind base_port, base_port+2, base_port+4 and base_port+6. |
| For example: |
| |
| UMPIRE_PORT=1234 $0 umpire run |
| |
| will change umpire base port to 1234 instead of ${UMPIRE_PORT}. |
| |
| commands for developers: |
| $0 umpire destroy |
| Destroy Umpire container. |
| |
| $0 umpire shell |
| Invoke a shell inside Umpire container. |
| |
| $0 umpire test [args...] |
| Run integration test for Umpire docker. This would take a while. |
| Extra arguments would be passed to the test script. |
| __EOF__ |
| } |
| |
| umpire_main() { |
| case "$1" in |
| run) |
| do_umpire_run |
| ;; |
| destroy) |
| do_umpire_destroy |
| ;; |
| shell) |
| do_umpire_shell |
| ;; |
| test) |
| shift |
| do_umpire_test "$@" |
| ;; |
| *) |
| umpire_usage |
| exit 1 |
| ;; |
| esac |
| } |
| |
| goofy_usage() { |
| cat << __EOF__ |
| Run the test harness and UI "Goofy" in Docker environment. |
| |
| Most subcommands support an optional "CROS_TEST_DOCKER_IMAGE" argument, which is |
| the Docker image repository name when you have imported a Chromium OS image |
| using '${SCRIPT_DIR}/image_tool docker -i IMAGE'. If you have only one image |
| installed, that image will be selected automatically. |
| |
| commands: |
| $0 goofy help |
| Show this help message. |
| |
| $0 goofy try [CROS_TEST_DOCKER_IMAGE] [-- [arg1 [arg2 ...]]] |
| Quickly run the Goofy from source tree. |
| |
| This command will try to run Goofy directly using your local source tree. |
| You have to first build goofy.js and locale folders manually, by running |
| following commands inside chroot: |
| |
| make |
| make po |
| |
| Then, you can start Goofy by 'try' command, and access to the web UI in |
| GOOFY_PORT (default ${GOOFY_PORT}). For example: |
| |
| GOOFY_PORT=1234 $0 goofy try |
| |
| $0 goofy shell [CROS_TEST_DOCKER_IMAGE] |
| Starts a shell to install and run Goofy manually. |
| |
| Unlike 'try' command, the 'shell' does not need source tree. Instead it |
| will allocate an empty folder in ${HOST_GOOFY_DIR} for you to install |
| a full Goofy toolkit installer, and then Goofy from there manually. |
| __EOF__ |
| } |
| |
| # Run Goofy inside Docker. |
| goofy_main() { |
| local try=false |
| case "$1" in |
| shell) |
| shift |
| ;; |
| try) |
| try=true |
| shift |
| ;; |
| *) |
| goofy_usage |
| exit 1 |
| ;; |
| esac |
| |
| check_docker |
| |
| # Decide CROS_TEST_DOCKER_IMAGE |
| local name="" |
| if [ "$#" -gt 0 -a "$1" != "--" ]; then |
| name="$1" |
| shift |
| else |
| # Try to find existing images. |
| local all_images="$( \ |
| ${DOCKER} images "cros/*_test" --format "{{.Repository}}" | uniq)" |
| case "$(echo "${all_images}" | wc -w)" in |
| 1) |
| name="${all_images}" |
| ;; |
| 0) |
| ;; |
| *) |
| die "Multiple images found, you have to specify one: " ${all_images} |
| ;; |
| esac |
| fi |
| if [ "$#" -gt 0 ]; then |
| if [ "$1" != "--" ]; then |
| goofy_usage |
| exit 1 |
| fi |
| shift |
| fi |
| # Normalize name and check if the image exists. |
| if [ -z "${name}" ]; then |
| die "Need Docker image from Chromium OS test image (image_tool docker)." |
| elif [ "${name##*/}" = "${name}" ]; then |
| name="cros/${name}" |
| fi |
| if [ -z "$(${DOCKER} images "${name}" --format '{{.Repository}}')" ]; then |
| die "Docker image does not exist: ${name}" |
| fi |
| |
| ensure_dir "${HOST_GOOFY_DIR}/var_factory" |
| ensure_dir_acl "${HOST_SHARED_DIR}" |
| |
| local locale_dir="${FACTORY_DIR}/build/locale" |
| local commands=() |
| |
| if "${try}"; then |
| if [ ! -e "${locale_dir}" ]; then |
| die "Please do 'make po' in chroot first." |
| fi |
| if [ ! -e "${FACTORY_DIR}/py/goofy/static/js/goofy.js" ]; then |
| die "Please do 'make default' in chroot first." |
| fi |
| # TODO(hungte) Support board overlay. |
| commands=( |
| "--volume" "${FACTORY_DIR}:/usr/local/factory" |
| "--volume" "${FACTORY_DIR}/build/locale:/usr/local/factory/locale" |
| "--env" "PYTHONDONTWRITEBYTECODE=1" |
| "${name}" "/usr/local/factory/bin/goofy_docker" "${@}") |
| echo ">> Starting Docker image ${name} in http://localhost:${GOOFY_PORT} .." |
| else |
| ensure_dir "${HOST_GOOFY_DIR}/local_factory" |
| if [ ! -e "${HOST_GOOFY_DIR}/local_factory/TOOLKIT_VERSION" ]; then |
| echo "You have to first manually install a toolkit." |
| echo "Copy the install_factory_toolkit.run to ${HOST_SHARED_DIR}" |
| echo " and then execute /mnt/install_factory_toolkit.run inside docker." |
| fi |
| echo "To start Goofy, run '${DOCKER_BASE_DIR}/bin/goofy_docker'." |
| commands=( |
| "--volume" "${HOST_GOOFY_DIR}/local_factory:/usr/local/factory" |
| "${name}" "bash") |
| fi |
| |
| ${DOCKER} run \ |
| --interactive \ |
| --tty \ |
| --rm \ |
| --name "goofy_${name##*/}" \ |
| --volume "${HOST_SHARED_DIR}:/mnt" \ |
| --volume "${HOST_GOOFY_DIR}/var_factory:/var/factory" \ |
| --publish "${GOOFY_PORT}:4012" \ |
| --tmpfs "/run:rw,size=16384k" \ |
| --tmpfs /var/log \ |
| "${commands[@]}" |
| } |
| |
| # Section for Overlord subcommand |
| do_overlord_setup() { |
| check_docker |
| |
| local overlord_setup_container_name="overlord_setup" |
| |
| echo "Doing setup for Overlord, you'll be asked for root permission ..." |
| sudo rm -rf "${HOST_OVERLORD_DIR}" |
| ensure_dir "${HOST_OVERLORD_CONFIG_DIR}" |
| ensure_dir_acl "${HOST_SHARED_DIR}" |
| |
| echo "Running setup script ..." |
| echo |
| |
| ${DOCKER} run \ |
| --interactive \ |
| --tty \ |
| --rm \ |
| --name "${overlord_setup_container_name}" \ |
| --volume "${HOST_OVERLORD_CONFIG_DIR}:${DOCKER_OVERLORD_CONFIG_DIR}" \ |
| "${DOCKER_IMAGE_NAME}" \ |
| "${DOCKER_OVERLORD_DIR}/setup.sh" || \ |
| (echo "Setup failed... removing Overlord settings."; \ |
| sudo rm -rf "${HOST_OVERLORD_DIR}"; \ |
| die "Overlord setup failed.") |
| |
| # Copy the certificate to script directory, and set it's permission to all |
| # readable, so it's easier to use (since the file is owned by root). |
| sudo cp "${HOST_OVERLORD_DIR}/config/cert.pem" "${SCRIPT_DIR}/cert.pem" |
| sudo chmod 644 "${SCRIPT_DIR}/cert.pem" |
| |
| echo |
| echo "Setup done!" |
| echo "You can find the generated certificate at ${SCRIPT_DIR}/cert.pem" |
| } |
| |
| do_overlord_run() { |
| check_docker |
| |
| local overlord_container_name="overlord" |
| local overlord_lan_disc_container_name="overlord_lan_disc" |
| |
| stop_and_remove_container "${overlord_container_name}" |
| stop_and_remove_container "${overlord_lan_disc_container_name}" |
| |
| if [ ! -d "${HOST_OVERLORD_DIR}" ]; then |
| do_overlord_setup |
| fi |
| |
| ${DOCKER} run \ |
| --detach \ |
| --restart unless-stopped \ |
| --name "${overlord_container_name}" \ |
| --volume "${HOST_OVERLORD_CONFIG_DIR}:${DOCKER_OVERLORD_CONFIG_DIR}" \ |
| --volume "${HOST_SHARED_DIR}:/mnt" \ |
| --publish "4455:4455" \ |
| --publish "${OVERLORD_HTTP_PORT}:9000" \ |
| --workdir "${DOCKER_OVERLORD_DIR}" \ |
| "${DOCKER_IMAGE_NAME}" \ |
| "./overlordd" -tls "config/cert.pem,config/key.pem" \ |
| -htpasswd-path "config/overlord.htpasswd" -no-lan-disc || \ |
| (echo "Removing stale container due to error ..."; \ |
| ${DOCKER} rm "${overlord_container_name}"; \ |
| die "Can't start overlord docker. Possibly wrong port binding?") |
| |
| # The Overlord LAN discovery need to be run with --net host. |
| ${DOCKER} run \ |
| --detach \ |
| --restart unless-stopped \ |
| --name "${overlord_lan_disc_container_name}" \ |
| --workdir "${DOCKER_OVERLORD_DIR}" \ |
| --net host \ |
| "${DOCKER_IMAGE_NAME}" \ |
| "./overlord_lan_disc" -lan-disc-iface "${OVERLORD_LAN_DISC_IFACE}"|| \ |
| (echo "Removing stale container due to error ..."; \ |
| ${DOCKER} rm "${overlord_lan_disc_container_name}"; \ |
| die "Can't start overlord lan discovery docker.") |
| } |
| |
| overlord_usage() { |
| cat << __EOF__ |
| Overlord: The Next-Gen Factory Monitor |
| |
| commands: |
| $0 overlord help |
| Show this help message. |
| |
| $0 overlord run |
| Create and start Overlord containers. |
| |
| You can change the Overlord http port (default ${OVERLORD_HTTP_PORT}) by |
| assigning the OVERLORD_HTTP_PORT environment variable. |
| For example: |
| |
| OVERLORD_HTTP_PORT=9090 $0 overlord run |
| |
| will bind port 9090 instead of ${OVERLORD_HTTP_PORT}. |
| |
| commands for developers: |
| $0 overlord setup |
| Run first-time setup for Overlord. Would reset everything in config |
| directory to the one in docker image. |
| __EOF__ |
| } |
| |
| overlord_main() { |
| case "$1" in |
| run) |
| do_overlord_run |
| ;; |
| setup) |
| do_overlord_setup |
| ;; |
| *) |
| overlord_usage |
| exit 1 |
| ;; |
| esac |
| } |
| |
| do_dev_run() { |
| check_docker |
| |
| do_build |
| |
| local docker_db_dir="/var/db/factory/dome" |
| local db_filename="db.sqlite3" |
| local docker_log_dir="/var/log/dome" |
| local host_log_dir="${HOST_DOME_DIR}/log" |
| local builder_container_name="dome_builder" |
| |
| # stop and remove old containers |
| do_dev_destroy |
| |
| do_prepare_dome |
| |
| echo "Copying node_modules into host directory ..." |
| ${DOCKER} create --name "${builder_container_name}" \ |
| "${DOME_BUILDER_IMAGE_NAME}" |
| ${DOCKER} cp \ |
| "${builder_container_name}:${DOCKER_DOME_FRONTEND_DIR}/node_modules" \ |
| "${DOME_DIR}/frontend" |
| ${DOCKER} rm "${builder_container_name}" |
| |
| ${DOCKER} network create "${DOME_DEV_DOCKER_NETWORK_NAME}" |
| |
| # Start dev server for frontend code. |
| ${DOCKER} run \ |
| --detach \ |
| --restart unless-stopped \ |
| --name "${DOME_DEV_FRONTEND_CONTAINER_NAME}" \ |
| --network "${DOME_DEV_DOCKER_NETWORK_NAME}" \ |
| --volume "${DOME_DIR}:${DOCKER_DOME_DIR}" \ |
| ${DOCKER_LOCALTIME_VOLUME} \ |
| --workdir "${DOCKER_DOME_FRONTEND_DIR}" \ |
| "${DOME_BUILDER_IMAGE_NAME}" \ |
| npm run dev |
| |
| # start django development server. |
| ${DOCKER} run \ |
| --detach \ |
| --restart unless-stopped \ |
| --name "${DOME_DEV_DJANGO_CONTAINER_NAME}" \ |
| --network "${DOME_DEV_DOCKER_NETWORK_NAME}" \ |
| --volume /var/run/docker.sock:/var/run/docker.sock \ |
| --env HOST_SHARED_DIR="${HOST_SHARED_DIR}" \ |
| --env HOST_UMPIRE_DIR="${HOST_UMPIRE_DIR}" \ |
| --env HOST_TFTP_DIR="${HOST_TFTP_DIR}" \ |
| --env HOST_LOCALTIME_PATH="${HOST_LOCALTIME_PATH}" \ |
| --env DOME_DEV_SERVER="1" \ |
| --env PYTHONDONTWRITEBYTECODE="1" \ |
| --volume /run \ |
| --volume "${DOME_DIR}:${DOCKER_DOME_DIR}" \ |
| --volume "${HOST_DOME_DIR}/${db_filename}:${docker_db_dir}/${db_filename}" \ |
| --volume "${host_log_dir}:${docker_log_dir}" \ |
| --volume "${HOST_TFTP_DIR}:${DOCKER_TFTP_DIR_IN_DOME}" \ |
| --volume "${HOST_UMPIRE_DIR}:${DOCKER_UMPIRE_DIR_IN_DOME}" \ |
| --volume "${HOST_SHARED_TMP_VOLUME}:${DOCKER_SHARED_TMP_DIR}" \ |
| ${DOCKER_LOCALTIME_VOLUME} \ |
| --workdir "${DOCKER_DOME_DIR}" \ |
| "${DOCKER_IMAGE_NAME}" \ |
| python manage.py runserver 0:8080 |
| |
| # start nginx |
| ${DOCKER} run \ |
| --detach \ |
| --restart unless-stopped \ |
| --name "${DOME_DEV_NGINX_CONTAINER_NAME}" \ |
| --network "${DOME_DEV_DOCKER_NETWORK_NAME}" \ |
| --volumes-from "${DOME_DEV_DJANGO_CONTAINER_NAME}" \ |
| --publish "127.0.0.1:${DOME_DEV_PORT}:80" \ |
| "${DOCKER_IMAGE_NAME}" \ |
| nginx -g "daemon off;" -c "${DOCKER_DOME_DIR}/nginx.dev.conf" |
| |
| echo |
| echo "Dome Dev server is running!" |
| echo "Open the browser to http://localhost:${DOME_DEV_PORT}/ and enjoy!" |
| } |
| |
| do_dev_destroy() { |
| check_docker |
| |
| stop_and_remove_container "${DOME_DEV_FRONTEND_CONTAINER_NAME}" |
| stop_and_remove_container "${DOME_DEV_DJANGO_CONTAINER_NAME}" |
| stop_and_remove_container "${DOME_DEV_NGINX_CONTAINER_NAME}" |
| ${DOCKER} network rm "${DOME_DEV_DOCKER_NETWORK_NAME}" 2>/dev/null || true |
| } |
| |
| dev_usage() { |
| cat << __EOF__ |
| Dome development server. |
| |
| The server would use py/dome/{frontend,backend} on host directly, and |
| automatically reload when file changed. |
| |
| commands: |
| $0 dev help |
| Show this help message. |
| |
| $0 dev run |
| Create and start development server for Dome. |
| |
| You can change the listening port (default ${DOME_DEV_PORT}) by assigning |
| the DOME_DEV_PORT environment variable. For example: |
| |
| DOME_DEV_PORT=1234 $0 dev run |
| |
| will bind port 1234 instead of ${DOME_DEV_PORT}. |
| |
| $0 dev destroy |
| Stop and remove the development server. |
| __EOF__ |
| } |
| |
| dev_main() { |
| case "$1" in |
| "") |
| do_dev_run |
| ;; |
| run) |
| do_dev_run |
| ;; |
| destroy) |
| do_dev_destroy |
| ;; |
| *) |
| dev_usage |
| exit 1 |
| ;; |
| esac |
| } |
| |
| # Section for main commands |
| do_pull() { |
| echo "Pulling Factory Server Docker image ..." |
| if [[ ! -f "${DOCKER_IMAGE_FILEPATH}" ]]; then |
| curl -L --fail "${PREBUILT_IMAGE_DIR_URL}/${DOCKER_IMAGE_FILENAME}" \ |
| -o "${DOCKER_IMAGE_FILEPATH}" || rm -f "${DOCKER_IMAGE_FILEPATH}" |
| [ -f "${DOCKER_IMAGE_FILEPATH}" ] || \ |
| die "Failed to pull Factory Server Docker image" |
| fi |
| echo "Finished pulling Factory Server Docker image" |
| |
| # TODO(littlecvr): make a single, self-executable file. |
| echo |
| echo "All finished, please copy: " |
| echo " 1. $(basename "$0") (this script)" |
| echo " 2. ${DOCKER_IMAGE_FILENAME}" |
| echo "to the target computer." |
| } |
| |
| do_install() { |
| check_docker |
| |
| ${DOCKER} load <"${DOCKER_IMAGE_FILEPATH}" |
| } |
| |
| do_prepare_dome() { |
| check_docker |
| |
| local docker_db_dir="/var/db/factory/dome" |
| local db_filename="db.sqlite3" |
| local docker_log_dir="/var/log/dome" |
| local host_log_dir="${HOST_DOME_DIR}/log" |
| |
| # make sure database file exists or mounting volume will fail |
| if [[ ! -f "${HOST_DOME_DIR}/${db_filename}" ]]; then |
| echo "Creating docker shared folder (${HOST_DOME_DIR})," |
| echo "and database file, you'll be asked for root permission ..." |
| ensure_dir "${HOST_DOME_DIR}" |
| sudo touch "${HOST_DOME_DIR}/${db_filename}" |
| ensure_dir_acl "${HOST_SHARED_DIR}" |
| fi |
| |
| # Migrate the database if needed (won't remove any data if the database |
| # already exists, but will apply the schema changes). This command may ask |
| # user questions and need the user input, so make it interactive. |
| ${DOCKER} run \ |
| --rm \ |
| --interactive \ |
| --tty \ |
| --volume "${HOST_DOME_DIR}/${db_filename}:${docker_db_dir}/${db_filename}" \ |
| --volume "${host_log_dir}:${docker_log_dir}" \ |
| --workdir "${DOCKER_DOME_DIR}" \ |
| "${DOCKER_IMAGE_NAME}" \ |
| python manage.py migrate |
| |
| # Clear all temporary uploaded file records. These files were uploaded to |
| # container but not in a volume, so they will be gone once the container has |
| # been removed. |
| ${DOCKER} run \ |
| --rm \ |
| --volume "${HOST_DOME_DIR}/${db_filename}:${docker_db_dir}/${db_filename}" \ |
| --volume "${host_log_dir}:${docker_log_dir}" \ |
| --workdir "${DOCKER_DOME_DIR}" \ |
| "${DOCKER_IMAGE_NAME}" \ |
| python manage.py shell --command \ |
| "import backend; backend.models.TemporaryUploadedFile.objects.all().delete()" |
| |
| # Restart all old umpire instances. |
| ${DOCKER} run \ |
| --rm \ |
| --volume /var/run/docker.sock:/var/run/docker.sock \ |
| --volume "${HOST_DOME_DIR}/${db_filename}:${docker_db_dir}/${db_filename}" \ |
| --volume "${host_log_dir}:${docker_log_dir}" \ |
| --workdir "${DOCKER_DOME_DIR}" \ |
| "${DOCKER_IMAGE_NAME}" \ |
| python manage.py restart_old_umpire |
| } |
| |
| do_run() { |
| check_docker |
| |
| # stop and remove old containers |
| stop_and_remove_container "${DOME_UWSGI_CONTAINER_NAME}" |
| stop_and_remove_container "${DOME_NGINX_CONTAINER_NAME}" |
| |
| do_prepare_dome |
| |
| local docker_db_dir="/var/db/factory/dome" |
| local db_filename="db.sqlite3" |
| local docker_log_dir="/var/log/dome" |
| local host_log_dir="${HOST_DOME_DIR}/log" |
| |
| # start uwsgi, the bridge between django and nginx |
| # Note 'docker' currently reads from '/var/run/docker.sock', which does not |
| # follow FHS 3.0 |
| ${DOCKER} run \ |
| --detach \ |
| --restart unless-stopped \ |
| --name "${DOME_UWSGI_CONTAINER_NAME}" \ |
| --volume /var/run/docker.sock:/var/run/docker.sock \ |
| --env HOST_SHARED_DIR="${HOST_SHARED_DIR}" \ |
| --env HOST_UMPIRE_DIR="${HOST_UMPIRE_DIR}" \ |
| --env HOST_TFTP_DIR="${HOST_TFTP_DIR}" \ |
| --env HOST_LOCALTIME_PATH="${HOST_LOCALTIME_PATH}" \ |
| --volume /run \ |
| --volume "${HOST_DOME_DIR}/${db_filename}:${docker_db_dir}/${db_filename}" \ |
| --volume "${host_log_dir}:${docker_log_dir}" \ |
| --volume "${HOST_TFTP_DIR}:${DOCKER_TFTP_DIR_IN_DOME}" \ |
| --volume "${HOST_UMPIRE_DIR}:${DOCKER_UMPIRE_DIR_IN_DOME}" \ |
| --volume "${HOST_SHARED_TMP_VOLUME}:${DOCKER_SHARED_TMP_DIR}" \ |
| ${DOCKER_LOCALTIME_VOLUME} \ |
| --workdir "${DOCKER_DOME_DIR}" \ |
| "${DOCKER_IMAGE_NAME}" \ |
| uwsgi --ini uwsgi.ini |
| |
| # start nginx |
| ${DOCKER} run \ |
| --detach \ |
| --restart unless-stopped \ |
| --name "${DOME_NGINX_CONTAINER_NAME}" \ |
| --volumes-from "${DOME_UWSGI_CONTAINER_NAME}" \ |
| --publish "${DOME_PORT}:80" \ |
| --workdir "${DOCKER_DOME_DIR}" \ |
| "${DOCKER_IMAGE_NAME}" \ |
| nginx -g "daemon off;" |
| |
| echo |
| echo "Dome is running!" |
| echo "Open the browser to http://localhost:${DOME_PORT}/ and enjoy!" |
| } |
| |
| do_build_dome_deps() { |
| check_docker |
| |
| local builder_output_file="$1" |
| local builder_workdir="${DOCKER_DOME_FRONTEND_DIR}" |
| local builder_dockerfile="${DOME_DIR}/docker/Dockerfile.builder" |
| local builder_container_name="dome_builder" |
| local builder_image_name="${DOME_BUILDER_IMAGE_NAME}" |
| |
| # build the dome builder image |
| ${DOCKER} build \ |
| --file "${builder_dockerfile}" \ |
| --tag "${builder_image_name}" \ |
| --build-arg workdir="${builder_workdir}" \ |
| --build-arg output_file="${builder_output_file}" \ |
| "${DOME_DIR}" |
| |
| # copy the builder's output from container to host |
| mkdir -p "${BUILD_DIR}" |
| ${DOCKER} create --name "${builder_container_name}" "${builder_image_name}" |
| ${DOCKER} cp \ |
| "${builder_container_name}:${builder_workdir}/build/${builder_output_file}" \ |
| "${BUILD_DIR}" |
| ${DOCKER} rm "${builder_container_name}" |
| } |
| |
| do_build_overlord() { |
| # We're using alpine for factory_server docker image, which is using musl |
| # libc instead of standard glibc, causing overlord compiled on host/chroot |
| # not able to run inside the docker image. So we have to compile it inside |
| # golang:alpine too. |
| check_docker |
| |
| local builder_output_file="$1" |
| local builder_workdir="/tmp/build" |
| local builder_dockerfile="${OVERLORD_DIR}/Dockerfile" |
| local builder_container_name="overlord_builder" |
| local builder_image_name="cros/overlord-builder" |
| |
| # build the overlord builder image |
| ${DOCKER} build \ |
| --file "${builder_dockerfile}" \ |
| --tag "${builder_image_name}" \ |
| --build-arg workdir="${builder_workdir}" \ |
| --build-arg output_file="${builder_output_file}" \ |
| "${FACTORY_DIR}" |
| |
| # copy the builder's output from container to host |
| mkdir -p "${BUILD_DIR}" |
| ${DOCKER} create --name "${builder_container_name}" "${builder_image_name}" |
| ${DOCKER} cp \ |
| "${builder_container_name}:${builder_workdir}/${builder_output_file}" \ |
| "${BUILD_DIR}" |
| ${DOCKER} rm "${builder_container_name}" |
| } |
| |
| do_build() { |
| local release_mode="$1" |
| local is_local=1 |
| |
| if [ "${release_mode}" = "publish" ]; then |
| is_local=0 |
| fi |
| |
| check_docker |
| |
| local dome_builder_output_file="frontend.tar" |
| local overlord_output_file="overlord.tar.gz" |
| |
| do_build_dome_deps "${dome_builder_output_file}" |
| do_build_overlord "${overlord_output_file}" |
| |
| local dockerfile="${SCRIPT_DIR}/Dockerfile" |
| |
| fetch_resource "${BUILD_DIR}/docker.tgz" \ |
| "${RESOURCE_DOCKER_URL}" "${RESOURCE_DOCKER_SHA1}" |
| fetch_resource "${BUILD_DIR}/pixz.tbz2" \ |
| "${RESOURCE_PIXZ_URL}" "${RESOURCE_PIXZ_SHA1}" |
| |
| if check_git_status; then |
| NEW_DOCKER_IMAGE_GITHASH="$(get_git_hash)" |
| else |
| # There are some uncommitted changes. |
| NEW_DOCKER_IMAGE_GITHASH="*$(get_git_hash)" |
| fi |
| NEW_DOCKER_IMAGE_TIMESTAMP="$(date '+%Y%m%d%H%M%S')" |
| |
| # need to make sure we're using the same version of docker inside the container |
| ${DOCKER} build \ |
| --file "${dockerfile}" \ |
| --tag "${DOCKER_IMAGE_NAME}" \ |
| --build-arg dome_dir="${DOCKER_DOME_DIR}" \ |
| --build-arg server_dir="${DOCKER_BASE_DIR}" \ |
| --build-arg instalog_dir="${DOCKER_INSTALOG_DIR}" \ |
| --build-arg umpire_dir_in_dome="${DOCKER_UMPIRE_DIR_IN_DOME}" \ |
| --build-arg dome_builder_output_file="${dome_builder_output_file}" \ |
| --build-arg overlord_output_file="${overlord_output_file}" \ |
| --build-arg docker_image_githash="${NEW_DOCKER_IMAGE_GITHASH}" \ |
| --build-arg docker_image_islocal="${is_local}" \ |
| --build-arg docker_image_timestamp="${NEW_DOCKER_IMAGE_TIMESTAMP}" \ |
| "${FACTORY_DIR}" |
| |
| echo "${DOCKER_IMAGE_NAME} image successfully built." |
| } |
| |
| do_get_changes() { |
| local changes_file="$1" |
| |
| # Check local modification. |
| if ! check_git_status; then |
| die "Your have uncommitted or untracked changes. " \ |
| "To publish you have to build without local modification." |
| fi |
| |
| local githash="$(get_git_hash)" |
| # Check if the head is already on remotes/cros/master. |
| local upstream="$(run_in_factory \ |
| git merge-base remotes/m/master "${githash}")" |
| if [ "${githash}" != "${upstream}" ]; then |
| die "Your latest commit was not merged to upstream (remotes/m/master)." \ |
| "To publish you have to build without local commits." |
| fi |
| |
| local source_list=( |
| setup/Dockerfile |
| setup/cros_docker.sh |
| sh/cros_payload.sh |
| py/dome |
| py/instalog |
| py/umpire |
| ':(exclude)py/umpire/client' |
| go/src/overlord |
| ) |
| |
| run_in_factory \ |
| git log --oneline ${DOCKER_IMAGE_GITHASH}.. "${source_list[@]}" | |
| grep -v " ${COMMIT_SUBJECT} " >"${changes_file}" |
| |
| if [ -s "${changes_file}" ]; then |
| echo "Server related changes since last build (${DOCKER_IMAGE_BUILD}):" |
| cat "${changes_file}" |
| echo "---" |
| else |
| # TODO(hungte) Make an option to allow forced publishing. |
| die "No server related changes since last publish." |
| fi |
| } |
| |
| do_update_docker_image_version() { |
| local script_file="$1" |
| local changes_file="$2" |
| |
| local branch_name="$(run_in_factory git rev-parse --abbrev-ref HEAD)" |
| if [ "${branch_name}" = "HEAD" ]; then |
| # We are on a detached HEAD - should start a new branch to work on so |
| # do_commit_docker_image_version can create a new commit. |
| # This is required before making any changes to ${script_file}. |
| run_in_factory repo start "cros_docker_${NEW_DOCKER_IMAGE_TIMESTAMP}" . |
| fi |
| |
| echo "Update publish information in $0..." |
| sed -i "s/${DOCKER_IMAGE_GITHASH}/${NEW_DOCKER_IMAGE_GITHASH}/; |
| s/${DOCKER_IMAGE_TIMESTAMP}/${NEW_DOCKER_IMAGE_TIMESTAMP}/" \ |
| "${script_file}" |
| } |
| |
| do_reload_docker_image_info() { |
| local script_file="$1" |
| DOCKER_IMAGE_GITHASH="$(sed -n 's/^DOCKER_IMAGE_GITHASH="\(.*\)"/\1/p' \ |
| "${script_file}")" |
| DOCKER_IMAGE_TIMESTAMP="$(sed -n 's/^DOCKER_IMAGE_TIMESTAMP="\(.*\)"/\1/p' \ |
| "${script_file}")" |
| set_docker_image_info |
| } |
| |
| do_commit_docker_image_release() { |
| local changes_file="$1" |
| |
| run_in_factory git commit -a -s -m \ |
| "${COMMIT_SUBJECT} ${DOCKER_IMAGE_TIMESTAMP}. |
| |
| A new release of cros_docker image on ${DOCKER_IMAGE_TIMESTAMP}, |
| built from source using hash ${DOCKER_IMAGE_GITHASH}. |
| Published as ${DOCKER_IMAGE_FILENAME}. |
| |
| Major changes: |
| $(cat "${changes_file}") |
| |
| BUG=chromium:679609 |
| TEST=None" |
| run_in_factory git show HEAD |
| echo "Uploading to gerrit..." |
| run_in_factory repo upload --cbr --no-verify . |
| } |
| |
| do_publish() { |
| check_gsutil |
| |
| local changes_file="$(mktemp)" |
| TEMP_OBJECTS=("${changes_file}" "${TEMP_OBJECTS[@]}") |
| do_get_changes "${changes_file}" |
| |
| do_build publish # make sure we have the newest image |
| |
| local script_file="$(realpath "$0")" |
| do_update_docker_image_version "${script_file}" "${changes_file}" || |
| die "Failed updating docker image version." |
| |
| do_reload_docker_image_info "${script_file}" |
| |
| local factory_server_image_url="${GSUTIL_BUCKET}/${DOCKER_IMAGE_FILENAME}" |
| if gsutil stat "${factory_server_image_url}" >/dev/null 2>&1; then |
| die "${DOCKER_IMAGE_FILENAME} is already on chromeos-localmirror" |
| fi |
| |
| local temp_dir="$(mktemp -d)" |
| TEMP_OBJECTS=("${temp_dir}" "${TEMP_OBJECTS[@]}") |
| |
| (cd "${temp_dir}"; do_save) |
| upload_to_localmirror "${temp_dir}/${DOCKER_IMAGE_FILENAME}" \ |
| "${factory_server_image_url}" |
| |
| do_commit_docker_image_release "${changes_file}" |
| } |
| |
| do_save() { |
| check_docker |
| check_xz |
| |
| echo "Saving Factory Server docker image to ${PWD}/${DOCKER_IMAGE_FILENAME} ..." |
| ${DOCKER} save "${DOCKER_IMAGE_NAME}" | ${XZ} >"${DOCKER_IMAGE_FILENAME}" |
| echo "Umpire docker image saved to ${PWD}/${DOCKER_IMAGE_FILENAME}" |
| } |
| |
| do_update() { |
| local script_file="$(realpath "$0")" |
| local temp_file="$(mktemp)" |
| local prefix="sudo" |
| TEMP_OBJECTS=("${temp_file}" "${TEMP_OBJECTS[@]}") |
| |
| curl -L --fail "${RESOURCE_CROS_DOCKER_URL}" | base64 --decode >"${temp_file}" |
| [ -s "${temp_file}" ] || die "Failed to download deployment script." |
| chmod +x "${temp_file}" |
| "${temp_file}" version || die "Failed to verify deployment script." |
| echo "Successfully downloaded latest deployment script." |
| if [ -w "${script_file}" ]; then |
| # No need to run sudo. |
| prefix= |
| fi |
| |
| # Can't keep running anything in the script. |
| exec ${prefix} mv -f "${temp_file}" "${script_file}" |
| } |
| |
| do_passwd() { |
| local username="${1:-admin}" |
| |
| check_docker |
| check_container_status "${DOME_UWSGI_CONTAINER_NAME}" |
| |
| ${DOCKER} exec -it "${DOME_UWSGI_CONTAINER_NAME}" \ |
| python manage.py changepassword "${username}" |
| } |
| |
| usage() { |
| cat << __EOF__ |
| Chrome OS Factory Server Deployment Script |
| |
| commands: |
| $0 help |
| Show this help message. |
| |
| $0 update |
| Update deployment script itself. |
| |
| $0 pull |
| Pull factory server docker images. |
| |
| $0 install |
| Load factory server docker images. |
| |
| $0 version |
| Print target factory server docker image file version. |
| |
| $0 run |
| Create and start Dome containers. |
| |
| You can change the listening port (default ${DOME_PORT}) by assigning the |
| DOME_PORT environment variable. For example: |
| |
| DOME_PORT=1234 $0 run |
| |
| will bind port 1234 instead of ${DOME_PORT}. |
| |
| $0 passwd [username] |
| Change the password of a given username. Default username is 'admin'. |
| |
| common use case: |
| - Run "$0 pull" to download docker images, and copy files listed on screen |
| to the target computer. |
| - Run "$0 install" on the target computer to load docker images. |
| - Run "$0 run" to start Dome. |
| - Run "$0 passwd" to change password (default: admin/test0000). |
| |
| commands for developers: |
| $0 build |
| Build factory server docker image. |
| |
| $0 publish |
| Build and publish factory server docker image to chromeos-localmirror. |
| |
| $0 save |
| Save factory server docker image to the current working directory. |
| |
| $0 goofy [subcommand] |
| Commands to run Goofy in Docker, see "$0 goofy help" for detail. |
| |
| $0 umpire [subcommand] |
| Commands for Umpire, see "$0 umpire help" for detail. |
| |
| $0 overlord [subcommand] |
| Commands for Overlord, see "$0 overlord help" for detail. |
| |
| $0 dev |
| Commands for development server for Dome, see "$0 dev help" for detail. |
| |
| __EOF__ |
| } |
| |
| main() { |
| case "$1" in |
| pull) |
| do_pull |
| ;; |
| install) |
| do_install |
| ;; |
| run) |
| do_run |
| ;; |
| passwd) |
| shift |
| do_passwd "$@" |
| ;; |
| build) |
| do_build |
| ;; |
| publish) |
| do_publish |
| ;; |
| save) |
| do_save |
| ;; |
| update) |
| do_update |
| ;; |
| version) |
| echo "Chrome OS Factory Server: ${DOCKER_IMAGE_VERSION}" |
| ;; |
| goofy) |
| shift |
| goofy_main "$@" |
| ;; |
| umpire) |
| shift |
| umpire_main "$@" |
| ;; |
| overlord) |
| shift |
| overlord_main "$@" |
| ;; |
| dev) |
| shift |
| dev_main "$@" |
| ;; |
| *) |
| usage |
| exit 1 |
| ;; |
| esac |
| } |
| |
| main "$@" |