From 8287fb66d32e8983b8b238609efa1d84c0d30daa Mon Sep 17 00:00:00 2001 From: Tyler Jewell Date: Mon, 6 Feb 2017 07:37:08 -0800 Subject: [PATCH] Refactor CLI: Faster boot, add `--help` global option, standardize command lifecycle (#4006) Signed-off-by: Tyler Jewell * multi container fixup * Update images * add --help parameter * move entrypoint * remove empty --- dockerfiles/base/Dockerfile | 2 + dockerfiles/base/build.sh | 1 - .../base/scripts/base/commands/cmd_action.sh | 25 +- .../base/scripts/base/commands/cmd_backup.sh | 19 +- .../base/scripts/base/commands/cmd_config.sh | 41 +- .../base/scripts/base/commands/cmd_destroy.sh | 20 +- .../base/scripts/base/commands/cmd_dir.sh | 27 +- .../scripts/base/commands/cmd_download.sh | 18 + .../base/scripts/base/commands/cmd_help.sh | 11 +- .../base/scripts/base/commands/cmd_info.sh | 43 +- .../base/scripts/base/commands/cmd_init.sh | 21 +- .../base/scripts/base/commands/cmd_network.sh | 109 ----- .../base/scripts/base/commands/cmd_offline.sh | 44 +- .../base/scripts/base/commands/cmd_restart.sh | 40 ++ .../base/scripts/base/commands/cmd_restore.sh | 14 +- .../base/scripts/base/commands/cmd_rmi.sh | 12 + .../base/scripts/base/commands/cmd_ssh.sh | 24 +- .../base/scripts/base/commands/cmd_start.sh | 91 ++-- .../base/scripts/base/commands/cmd_stop.sh | 60 +++ .../base/scripts/base/commands/cmd_sync.sh | 27 +- .../base/scripts/base/commands/cmd_test.sh | 21 +- .../base/scripts/base/commands/cmd_upgrade.sh | 24 +- .../base/scripts/base/commands/cmd_version.sh | 14 +- dockerfiles/base/scripts/base/curl.sh | 46 -- dockerfiles/base/scripts/base/library.sh | 447 ++++++------------ dockerfiles/base/scripts/base/paths.sh | 85 ---- dockerfiles/base/scripts/base/startup.sh | 6 +- .../{startup_funcs.sh => startup_01_init.sh} | 361 +++----------- .../{docker.sh => startup_02_pre_docker.sh} | 397 +++++++++++----- .../scripts/base/startup_03_pre_networking.sh | 244 ++++++++++ .../scripts/base/startup_04_pre_cli_init.sh | 309 ++++++++++++ .../base/scripts/base/startup_05_pre_exec.sh | 71 +++ .../scripts/entrypoint.sh} | 4 + dockerfiles/build.include | 8 +- dockerfiles/cli/Dockerfile | 2 +- dockerfiles/cli/scripts/cli.sh | 34 -- dockerfiles/cli/scripts/post_init.sh | 13 + .../scripts/{entrypoint.sh => pre_init.sh} | 11 +- dockerfiles/cli/version/5.3.0/images | 1 + dockerfiles/cli/version/latest/images | 1 + dockerfiles/cli/version/nightly/images | 1 + .../init/modules/che/manifests/init.pp | 8 + .../init/modules/che/templates/che.sh.erb | 251 ++++++++++ 43 files changed, 1906 insertions(+), 1102 deletions(-) delete mode 100644 dockerfiles/base/scripts/base/commands/cmd_network.sh create mode 100644 dockerfiles/base/scripts/base/commands/cmd_restart.sh create mode 100644 dockerfiles/base/scripts/base/commands/cmd_stop.sh delete mode 100644 dockerfiles/base/scripts/base/curl.sh delete mode 100644 dockerfiles/base/scripts/base/paths.sh rename dockerfiles/base/scripts/base/{startup_funcs.sh => startup_01_init.sh} (63%) rename dockerfiles/base/scripts/base/{docker.sh => startup_02_pre_docker.sh} (65%) create mode 100644 dockerfiles/base/scripts/base/startup_03_pre_networking.sh create mode 100644 dockerfiles/base/scripts/base/startup_04_pre_cli_init.sh create mode 100644 dockerfiles/base/scripts/base/startup_05_pre_exec.sh rename dockerfiles/{cli/scripts/cmd_empty.sh => base/scripts/entrypoint.sh} (86%) delete mode 100644 dockerfiles/cli/scripts/cli.sh create mode 100644 dockerfiles/cli/scripts/post_init.sh rename dockerfiles/cli/scripts/{entrypoint.sh => pre_init.sh} (77%) mode change 100755 => 100644 create mode 100644 dockerfiles/init/modules/che/templates/che.sh.erb diff --git a/dockerfiles/base/Dockerfile b/dockerfiles/base/Dockerfile index 201884bd240..633d44d0c74 100644 --- a/dockerfiles/base/Dockerfile +++ b/dockerfiles/base/Dockerfile @@ -34,4 +34,6 @@ RUN mkdir -p /version \ && docker -v COPY scripts/base /scripts/base/ +COPY scripts/entrypoint.sh /scripts/entrypoint.sh +RUN chmod u+x /scripts/entrypoint.sh diff --git a/dockerfiles/base/build.sh b/dockerfiles/base/build.sh index 007b04cc4c8..12133514c15 100755 --- a/dockerfiles/base/build.sh +++ b/dockerfiles/base/build.sh @@ -11,4 +11,3 @@ base_dir=$(cd "$(dirname "$0")"; pwd) init "$@" build - diff --git a/dockerfiles/base/scripts/base/commands/cmd_action.sh b/dockerfiles/base/scripts/base/commands/cmd_action.sh index 2f97445916a..d310a62522c 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_action.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_action.sh @@ -9,10 +9,31 @@ # Tyler Jewell - Initial Implementation # -cmd_action() { - debug $FUNCNAME +help_cmd_action() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} action [ACTION_PARAMETERS]\n" + text "\n" + text "Executes a REST action against ${CHE_MINI_PRODUCT_NAME} server or workspace.\n" + text "\n" + text "ACTIONS:\n" + text " create-start-workspace\n" + text " add-user\n" + text " remove-user\n" + text " execute-command\n" + text " list-workspaces\n" + text " workspace-ssh\n" + text " get-ssh-data\n" + text " graceful-stop\n" + text "\n" + text "Run '${CHE_IMAGE_FULLNAME} action' for action parameters" + text "\n" +} +pre_cmd_action() { # Not loaded as part of the init process to save on download time load_utilities_images_if_not_done +} + +cmd_action() { docker_run -it ${UTILITY_IMAGE_CHEACTION} "$@" } diff --git a/dockerfiles/base/scripts/base/commands/cmd_backup.sh b/dockerfiles/base/scripts/base/commands/cmd_backup.sh index dfe3348fb53..65a8dfe0d27 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_backup.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_backup.sh @@ -9,9 +9,22 @@ # Tyler Jewell - Initial Implementation # -cmd_backup() { - debug $FUNCNAME +help_cmd_backup() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} backup [PARAMETERS]\n" + text "\n" + text "Backup ${CHE_MINI_PRODUCT_NAME} configuration and user data\n" + text "\n" + text "PARAMETERS:\n" + text " --no-skip-data Excludes user data in /instance/data\n" + text "\n" +} +pre_cmd_backup() { + true +} + +cmd_backup() { # possibility to skip ${CHE_FORMAL_PRODUCT_NAME} projects backup SKIP_BACKUP_CHE_DATA=${1:-"--no-skip-data"} if [[ "${SKIP_BACKUP_CHE_DATA}" == "--skip-data" ]]; then @@ -46,7 +59,7 @@ cmd_backup() { $(cmd_backup_extra_args) \ ${BOOTSTRAP_IMAGE_ALPINE} sh -c "tar czf /root/backup/${CHE_BACKUP_FILE_NAME} -C /root${CHE_CONTAINER_ROOT} . --exclude='backup' --exclude='instance/dev' --exclude='instance/logs' ${TAR_EXTRA_EXCLUDE}" info "" - info "backup" "Codenvy data saved in ${CHE_HOST_BACKUP}/${CHE_BACKUP_FILE_NAME}" + info "backup" "${CHE_MINI_PRODUCT_NAME} data saved in ${CHE_HOST_BACKUP}/${CHE_BACKUP_FILE_NAME}" } cmd_backup_extra_args() { diff --git a/dockerfiles/base/scripts/base/commands/cmd_config.sh b/dockerfiles/base/scripts/base/commands/cmd_config.sh index 189bec61f6d..b0aef504360 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_config.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_config.sh @@ -9,8 +9,27 @@ # Tyler Jewell - Initial Implementation # -cmd_config_post_action() { - true +help_cmd_config() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} config [PARAMETERS]\n" + text "\n" + text "Generate a ${CHE_MINI_PRODUCT_NAME} runtime configuration into /instance. The configurator uses +values from your host, ${CHE_MINI_PRODUCT_NAME}.env, and optionally your local repository to generate +a runtime configuration used to start and stop ${CHE_MINI_PRODUCT_NAME}. A configuration is generated +before every execution of ${CHE_MINI_PRODUCT_NAME}. The configuration phase will download all Docker +images required to start or stop ${CHE_MINI_PRODUCT_NAME} to guarantee that the right images are cached +before execution. If you have mounted a local repository or assembly, the ${CHE_MINI_PRODUCT_NAME} Docker +images will use those binaries instead of their embedded ones.\n" + text "\n" + text "PARAMETERS:\n" + text " --no-force Updates images if matching tag not found in local cache\n" + text " --pull Uses 'docker pull' to check for new remote versions of images\n" + text " --force Uses 'docker rmi' and 'docker pull' to forcibly retrieve latest images\n" + text "\n" +} + +pre_cmd_config() { + true } cmd_config() { @@ -19,12 +38,12 @@ cmd_config() { # If the system is already initialized, but a user wants to update images, then re-download. FORCE_UPDATE=${1:-"--no-force"} if ! is_initialized; then - cmd_init $FORCE_UPDATE + cmd_lifecycle init $FORCE_UPDATE elif [[ "${FORCE_UPDATE}" == "--pull" ]] || \ [[ "${FORCE_UPDATE}" == "--force" ]]; then - cmd_download $FORCE_UPDATE + cmd_lifecycle download $FORCE_UPDATE elif is_nightly && ! is_fast && ! skip_pull; then - cmd_download --pull + cmd_lifecycle download --pull fi # If using a local repository, then we need to always perform an updated init with those files @@ -44,7 +63,6 @@ cmd_config() { # Replace certain environment file lines with their container counterparts info "config" "Customizing docker-compose for running in a container" - if local_repo || local_assembly; then # in development mode to avoid permissions issues we copy tomcat assembly to ${CHE_INSTANCE} @@ -74,8 +92,6 @@ cmd_config() { cp -r "$(echo ${CHE_CONTAINER_ASSEMBLY_FULL_PATH})/." \ "${CHE_CONTAINER_INSTANCE}/dev/${CHE_MINI_PRODUCT_NAME}-tomcat/" fi - - cmd_config_post_action } @@ -151,3 +167,12 @@ config_directory_is_empty() { return 0 fi } + +post_cmd_config() { + cmd_config_post_action +} + +cmd_config_post_action() { + true +} + diff --git a/dockerfiles/base/scripts/base/commands/cmd_destroy.sh b/dockerfiles/base/scripts/base/commands/cmd_destroy.sh index ef3d870b8eb..f7b2332b6c4 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_destroy.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_destroy.sh @@ -9,6 +9,22 @@ # Tyler Jewell - Initial Implementation # +help_cmd_destroy() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} destroy [PARAMETERS]\n" + text "\n" + text "Deletes a ${CHE_MINI_PRODUCT_NAME} installation\n" + text "\n" + text "PARAMETERS:\n" + text " --quiet Do not ask user for confirmation\n" + text " --cli Removes the 'cli.log'\n" + text "\n" +} + + +pre_cmd_destroy() { + true +} cmd_destroy_post_action() { true @@ -32,12 +48,12 @@ cmd_destroy() { esac done - WARNING="${RED}!!!${NC} Stopping services and ${RED}!!!${NC} deleting data ${RED}!!!${NC} this is unrecoverable ${RED}!!!${NC}" + WARNING="${YELLOW}!!!${RED} Stopping services and ${YELLOW}!!!${RED} deleting data ${YELLOW}!!!${RED} this is unrecoverable ${YELLOW}!!!${NC}" if ! confirm_operation "${WARNING}" "${QUIET}"; then return; fi - cmd_stop --force + cmd_lifecycle stop --skip:graceful info "destroy" "Deleting instance and config..." diff --git a/dockerfiles/base/scripts/base/commands/cmd_dir.sh b/dockerfiles/base/scripts/base/commands/cmd_dir.sh index cdc198a8183..73e7738ceeb 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_dir.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_dir.sh @@ -6,9 +6,27 @@ # http://www.eclipse.org/legal/epl-v10.html # -cmd_dir() { - debug $FUNCNAME +help_cmd_dir() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} dir COMMAND [PARAMETERS]\n" + text "\n" + text "Create a workspace using a local directory mounted to ':\chedir'\n" + text "\n" + text "COMMANDS:\n" + text " init Initializes an empty local directory with a new Chefile\n" + text " down Stops the workspace and ${CHE_MINI_PRODUCT_NAME} representing this directory\n" + text " ssh SSH into the workspace that represents the local directory\n" + text " status Reports on the runtime status of ${CHE_MINI_PRODUCT_NAME} and the workspace runtime\n" + text " up Starts ${CHE_MINI_PRODUCT_NAME} and creates a new workspace with a project from your local dir\n" + text "\n" +} +pre_cmd_dir() { + # Not loaded as part of the init process to save on download time + load_utilities_images_if_not_done +} + +cmd_dir() { CHE_LOCAL_REPO=false if [[ "${CHEDIR_MOUNT}" != "not set" ]]; then local HOST_FOLDER_TO_USE="${CHEDIR_MOUNT}" @@ -18,7 +36,6 @@ cmd_dir() { warning "':/chedir' not mounted - using ${DATA_MOUNT} as source location" fi - # Not loaded as part of the init process to save on download time - load_utilities_images_if_not_done - docker_run -it -v ${HOST_FOLDER_TO_USE}:${HOST_FOLDER_TO_USE} ${UTILITY_IMAGE_CHEDIR} ${HOST_FOLDER_TO_USE} "$@" + docker_run -it -v ${HOST_FOLDER_TO_USE}:${HOST_FOLDER_TO_USE} \ + ${UTILITY_IMAGE_CHEDIR} ${HOST_FOLDER_TO_USE} "$@" } diff --git a/dockerfiles/base/scripts/base/commands/cmd_download.sh b/dockerfiles/base/scripts/base/commands/cmd_download.sh index db48e9106e0..496bea673fa 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_download.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_download.sh @@ -9,6 +9,23 @@ # Tyler Jewell - Initial Implementation # +help_cmd_download() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} download [PARAMETERS]\n" + text "\n" + text "Downloads Docker images required to execute ${CHE_MINI_PRODUCT_NAME}\n" + text "\n" + text "PARAMETERS:\n" + text " --force Uses 'docker rmi' and 'docker pull' to forcibly retrieve latest images\n" + text " --no-force Updates images if matching tag not found in local cache\n" + text " --pull Uses 'docker pull' to check for new remote versions of images\n" + text "\n" +} + +pre_cmd_download() { + true +} + cmd_download() { FORCE_UPDATE=${1:-"--no-force"} @@ -23,3 +40,4 @@ cmd_download() { fi done } + diff --git a/dockerfiles/base/scripts/base/commands/cmd_help.sh b/dockerfiles/base/scripts/base/commands/cmd_help.sh index d6e0fa81738..93fb4c2e536 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_help.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_help.sh @@ -9,7 +9,16 @@ # Tyler Jewell - Initial Implementation # +help_cmd_help() { + text "\n" + text "Usage: ${CHE_IMAGE_FULLNAME} help" + text "\n" +} + cmd_help() { - usage + usage } +pre_cmd_help() { + true +} diff --git a/dockerfiles/base/scripts/base/commands/cmd_info.sh b/dockerfiles/base/scripts/base/commands/cmd_info.sh index 621052dc9a8..1fa516399cd 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_info.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_info.sh @@ -9,6 +9,25 @@ # Tyler Jewell - Initial Implementation # +help_cmd_info() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} info [PARAMETERS]\n" + text "\n" + text "Status, information, and support diagnostic bundles for ${CHE_MINI_PRODUCT_NAME}\n" + text "\n" + text "PARAMETERS:\n" + text " --all Prints info, runs networking tests, ad prepares diagnostic bundle\n" + text " --bundle Prepares diagnostic bundle for ${CHE_MINI_PRODUCT_NAME} and Docker\n" + text " --network Runs simulated network diagnostic to confirm network routing\n" + text " --print Default - displays status and configuration of ${CHE_MINI_PRODUCT_NAME}\n" + text "\n" +} + + +pre_cmd_info() { + true +} + cmd_info() { debug $FUNCNAME if [ $# -eq 0 ]; then @@ -132,12 +151,6 @@ print_info() { text " $SINGLE_IMAGE\n" done - # This seems like overkill for the output - #readarray STACK_IMAGE_LIST < /version/$CHE_VERSION/images-stacks - #for SINGLE_IMAGE in "${STACK_IMAGE_LIST[@]}"; do - #text " $SINGLE_IMAGE" - #done - if ! is_initialized; then text "${CHE_ENVIRONMENT_FILE}: not initialized\n" else @@ -149,3 +162,21 @@ print_info() { done fi } + +cmd_network() { + info "" + info "---------------------------------------" + info "-------- CONNECTIVITY TEST --------" + info "---------------------------------------" + + info "network" "${BOOTSTRAP_IMAGE_CHEIP}: ${CHE_HOST}" + + start_test_server + + info "network" "Browser => Workspace Agent (localhost): Connection $(test1 && echo "succeeded" || echo "failed")" + info "network" "Browser => Workspace Agent ($AGENT_EXTERNAL_IP): Connection $(test2 && echo "succeeded" || echo "failed")" + info "network" "Server => Workspace Agent (External IP): Connection $(test3 && echo "succeeded" || echo "failed")" + info "network" "Server => Workspace Agent (Internal IP): Connection $(test4 && echo "succeeded" || echo "failed")" + + stop_test_server +} diff --git a/dockerfiles/base/scripts/base/commands/cmd_init.sh b/dockerfiles/base/scripts/base/commands/cmd_init.sh index 629bef2ba60..82294ba408e 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_init.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_init.sh @@ -9,6 +9,25 @@ # Tyler Jewell - Initial Implementation # +help_cmd_init() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} init [PARAMETERS]\n" + text "\n" + text "Initializes a directory with a new ${CHE_MINI_PRODUCT_NAME} installation\n" + text "\n" + text "PARAMETERS:\n" + text " --accept-license If license acceptance required, auto accepts during installation\n" + text " --force Uses 'docker rmi' and 'docker pull' to forcibly retrieve latest images\n" + text " --no-force Updates images if matching tag not found in local cache\n" + text " --pull Uses 'docker pull' to check for new remote versions of images\n" + text " --reinit Reinitialize an existing installation overwriting defaults\n" + text "\n" +} + +pre_cmd_init() { + true +} + cmd_init() { # set an initial value for the flag @@ -50,7 +69,7 @@ cmd_init() { warning "($CHE_MINI_PRODUCT_NAME init): 'nightly' installations cannot be upgraded to non-nightly versions" fi - cmd_download $FORCE_UPDATE + cmd_lifecycle download $FORCE_UPDATE if require_license; then if [[ "${AUTO_ACCEPT_LICENSE}" = "false" ]]; then diff --git a/dockerfiles/base/scripts/base/commands/cmd_network.sh b/dockerfiles/base/scripts/base/commands/cmd_network.sh deleted file mode 100644 index 08942d9a20c..00000000000 --- a/dockerfiles/base/scripts/base/commands/cmd_network.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/bin/bash -# Copyright (c) 2012-2016 Codenvy, S.A. -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Eclipse Public License v1.0 -# which accompanies this distribution, and is available at -# http://www.eclipse.org/legal/epl-v10.html -# -# Contributors: -# Tyler Jewell - Initial Implementation -# - -cmd_network() { - debug $FUNCNAME - - info "" - info "---------------------------------------" - info "-------- CONNECTIVITY TEST --------" - info "---------------------------------------" - - info "network" "eclipse/che-ip: ${GLOBAL_HOST_IP}" - - start_test_server - - info "network" "Browser => Workspace Agent (localhost): Connection $(test1 && echo "succeeded" || echo "failed")" - info "network" "Browser => Workspace Agent ($AGENT_EXTERNAL_IP): Connection $(test2 && echo "succeeded" || echo "failed")" - info "network" "Server => Workspace Agent (External IP): Connection $(test3 && echo "succeeded" || echo "failed")" - info "network" "Server => Workspace Agent (Internal IP): Connection $(test4 && echo "succeeded" || echo "failed")" - - stop_test_server -} - -start_test_server() { - export AGENT_INTERNAL_PORT=80 - export AGENT_EXTERNAL_PORT=32768 - - # Start mini httpd server to run simulated tests - docker run -d -p $AGENT_EXTERNAL_PORT:$AGENT_INTERNAL_PORT --name fakeagent \ - ${UTILITY_IMAGE_ALPINE} httpd -f -p $AGENT_INTERNAL_PORT -h /etc/ >> "${LOGS}" - - export AGENT_INTERNAL_IP=$(docker inspect --format='{{.NetworkSettings.IPAddress}}' fakeagent) - export AGENT_EXTERNAL_IP=$CHE_HOST -} - -stop_test_server() { - # Remove httpd server - docker rm -f fakeagent >> "${LOGS}" -} - -test1() { - HTTP_CODE=$(curl -I localhost:${AGENT_EXTERNAL_PORT}/alpine-release \ - -s -o "${LOGS}" --connect-timeout 5 \ - --write-out '%{http_code}') || echo "28" >> "${LOGS}" - - if check_http_code $HTTP_CODE; then - return 0 - else - return 1 - fi -} - -test2() { - HTTP_CODE=$(curl -I ${AGENT_EXTERNAL_IP}:${AGENT_EXTERNAL_PORT}/alpine-release \ - -s -o "${LOGS}" --connect-timeout 5 \ - --write-out '%{http_code}') || echo "28" >> "${LOGS}" - - if check_http_code $HTTP_CODE; then - return 0 - else - return 1 - fi -} - -test3() { - HTTP_CODE=$(docker_run --name fakeserver \ - --entrypoint=curl \ - $(eval "echo \${IMAGE_${CHE_PRODUCT_NAME}}") \ - -I ${AGENT_EXTERNAL_IP}:${AGENT_EXTERNAL_PORT}/alpine-release \ - -s -o "${LOGS}" \ - --write-out '%{http_code}') - - if check_http_code $HTTP_CODE; then - return 0 - else - return 1 - fi -} - -test4() { - HTTP_CODE=$(docker_run --name fakeserver \ - --entrypoint=curl \ - $(eval "echo \${IMAGE_${CHE_PRODUCT_NAME}}") \ - -I ${AGENT_INTERNAL_IP}:${AGENT_INTERNAL_PORT}/alpine-release \ - -s -o "${LOGS}" \ - --write-out '%{http_code}') - - if check_http_code $HTTP_CODE; then - return 0 - else - return 1 - fi -} - -check_http_code() { - if [ "${1}" = "200" ]; then - return 0 - else - return 1 - fi -} \ No newline at end of file diff --git a/dockerfiles/base/scripts/base/commands/cmd_offline.sh b/dockerfiles/base/scripts/base/commands/cmd_offline.sh index cb869b227ca..dddf6fdab60 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_offline.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_offline.sh @@ -9,9 +9,33 @@ # Tyler Jewell - Initial Implementation # +help_cmd_offline() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} offline [PARAMETERS]\n" + text "\n" + text "Downloads and saves Docker images required to run ${CHE_MINI_PRODUCT_NAME} offline. Add the +'--offline' global parameter command to execute ${CHE_MINI_PRODUCT_NAME} in offline mode. You can optionally +download stack images used to start workspaces. Stack images are heavy and often larger than 1GB. You +can save them all or selectively choose stacks.\n" + text "\n" + text "PARAMETERS:\n" + text " --all-stacks Saves all stack images\n" + text " --list Lists all images that will be downloaded and saved\n" + text " --image: Downloads specific stack image\n" + text " --no-stacks Do not save any stack images\n" + text "\n" +} + + +pre_cmd_offline() { + true +} + cmd_offline() { # Read in optional stack images readarray -t STACK_IMAGE_LIST < /version/$CHE_VERSION/images-stacks + readarray -t BOOTSTRAP_IMAGE_LIST < ${SCRIPTS_BASE_CONTAINER_SOURCE_DIR}/images/images-bootstrap + readarray -t UTILITY_IMAGE_LIST < ${SCRIPTS_BASE_CONTAINER_SOURCE_DIR}/images/images-utilities # List all images to be saved if [[ $# -gt 0 ]] && [[ $1 = "--list" ]]; then @@ -19,14 +43,26 @@ cmd_offline() { info "offline" "Listing images to save for offline usage" info "" info "offline" "Always:" - info "offline" " CLI: ${CHE_IMAGE_FULLNAME}" + info "offline" " CLI: ${CHE_IMAGE_FULLNAME}" + + IFS=$'\n' + for SINGLE_IMAGE in $BOOTSTRAP_IMAGE_LIST; do + IMAGE_NAME=$(echo $SINGLE_IMAGE | cut -d'=' -f2) + info "offline" " BOOTSTRAP: ${IMAGE_NAME}" + done IFS=$'\n' for SINGLE_IMAGE in $IMAGE_LIST; do IMAGE_NAME=$(echo $SINGLE_IMAGE | cut -d'=' -f2) - info "offline" " CORE: ${IMAGE_NAME}" + info "offline" " SYSTEM: ${IMAGE_NAME}" done + IFS=$'\n' + for SINGLE_IMAGE in $UTILITY_IMAGE_LIST; do + IMAGE_NAME=$(echo $SINGLE_IMAGE | cut -d'=' -f2) + info "offline" " UTILITY: ${IMAGE_NAME}" + done + info "" info "offline" "Optional: (repeat --image: for stack, --all-stacks, or --no-stacks)" for STACK in $(seq 0 $((${#STACK_IMAGE_LIST[@]}-1))) @@ -38,7 +74,7 @@ cmd_offline() { fi # Make sure the images have been pulled and are in your local Docker registry - cmd_download + cmd_lifecycle download mkdir -p $CHE_CONTAINER_OFFLINE_FOLDER @@ -76,7 +112,7 @@ cmd_offline() { break shift ;; --no-stacks) - info "offline" " --no-stacks indicated...skipping" + info "offline" "--no-stacks indicated...skipping" break shift ;; --image:*|-i:*) diff --git a/dockerfiles/base/scripts/base/commands/cmd_restart.sh b/dockerfiles/base/scripts/base/commands/cmd_restart.sh new file mode 100644 index 00000000000..acc0c7e8cb5 --- /dev/null +++ b/dockerfiles/base/scripts/base/commands/cmd_restart.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# Copyright (c) 2012-2016 Codenvy, S.A. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Tyler Jewell - Initial Implementation +# + +help_cmd_restart() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} restart [PARAMETERS]\n" + text "\n" + text "Stops ${CHE_MINI_PRODUCT_NAME} and starts again\n" + text "\n" + text "PARAMETERS:\n" + text " --force Uses 'docker rmi' and 'docker pull' to forcibly retrieve latest images\n" + text " --no-force Updates images if matching tag not found in local cache\n" + text " --pull Uses 'docker pull' to check for new remote versions of images\n" + text " --skip:graceful Do not wait for confirmation that workspaces have stopped\n" + text " --skip:preflight Skip preflight checks\n" + text "\n" +} + +pre_cmd_restart() { + true +} + +cmd_restart() { + + info "restart" "Restarting..." + cmd_lifecycle stop ${@} + + # Need to remove any stop parameters from the command line otherwise the start will fail + set -- "${@/\-\-skip\:graceful/}" + + cmd_lifecycle start "${@}" +} diff --git a/dockerfiles/base/scripts/base/commands/cmd_restore.sh b/dockerfiles/base/scripts/base/commands/cmd_restore.sh index b143ee7915f..364d031a36b 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_restore.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_restore.sh @@ -9,6 +9,18 @@ # Tyler Jewell - Initial Implementation # +help_cmd_restore() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} restore\n" + text "\n" + text "Restores user data and recovers a ${CHE_MINI_PRODUCT_NAME} configuration\n" + text "\n" +} + +pre_cmd_restore() { + true +} + cmd_restore_pre_action() { true } @@ -28,7 +40,7 @@ cmd_restore() { fi if get_server_container_id "${CHE_SERVER_CONTAINER_NAME}" >> "${LOGS}" 2>&1; then - error "${CHE_FORMAL_PRODUCT_NAME} is running. Stop before performing a restore. Aborting" + error "${CHE_FORMAL_PRODUCT_NAME} is running. Stop before performing a restore." return; fi diff --git a/dockerfiles/base/scripts/base/commands/cmd_rmi.sh b/dockerfiles/base/scripts/base/commands/cmd_rmi.sh index 4886709312f..5b7f5b65b5f 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_rmi.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_rmi.sh @@ -9,6 +9,18 @@ # Tyler Jewell - Initial Implementation # +help_cmd_rmi() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} rmi\n" + text "\n" + text "Removes bootstrap, utility, and system Docker images used to run ${CHE_MINI_PRODUCT_NAME}\n" + text "\n" +} + +pre_cmd_rmi() { + true +} + cmd_rmi() { info "rmi" "Checking registry for version '$CHE_VERSION' images" if ! has_version_registry $CHE_VERSION; then diff --git a/dockerfiles/base/scripts/base/commands/cmd_ssh.sh b/dockerfiles/base/scripts/base/commands/cmd_ssh.sh index 2dadcae17c0..ab1c95b9c4c 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_ssh.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_ssh.sh @@ -9,7 +9,27 @@ # Tyler Jewell - Initial Implementation # +help_cmd_ssh() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} ssh WORKSPACE [MACHINE] [PARAMETERS]\n" + text "\n" + text "Connect to a workspace in ${CHE_MINI_PRODUCT_NAME} over SSH\n" + text "\n" + text "WORKSPACE: Accepts workspace name, ID, or namespace:ws-name\n" + text " List all workspaces with 'action list-workspaces'\n" + text "\n" + text "MACHINE: Choose machine (default is dev machine) if workspace as multiple containers\n" + text "\n" + text "PARAMETERS:\n" + text " --url Location of ${CHE_MINI_PRODUCT_NAME}\n" + text " --user User name of ${CHE_MINI_PRODUCT_NAME} if accessing authenticated system\n" + text " --password Password of ${CHE_MINI_PRODUCT_NAME} if accessing authenticated system\n" +} + +pre_cmd_ssh() { + true +} + cmd_ssh() { - debug $FUNCNAME - cmd_action "workspace-ssh" "$@" + cmd_lifecycle action "workspace-ssh" "$@" } diff --git a/dockerfiles/base/scripts/base/commands/cmd_start.sh b/dockerfiles/base/scripts/base/commands/cmd_start.sh index a75c8fdd5c3..a9bf0fb138f 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_start.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_start.sh @@ -9,9 +9,25 @@ # Tyler Jewell - Initial Implementation # -cmd_start() { - debug $FUNCNAME +help_cmd_start() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} start [PARAMETERS]\n" + text "\n" + text "Starts ${CHE_MINI_PRODUCT_NAME} and verifies its operation\n" + text "\n" + text "PARAMETERS:\n" + text " --force Uses 'docker rmi' and 'docker pull' to forcibly retrieve latest images\n" + text " --no-force Updates images if matching tag not found in local cache\n" + text " --pull Uses 'docker pull' to check for new remote versions of images\n" + text " --skip:preflight Skip preflight checks\n" + text "\n" +} +pre_cmd_start() { + true +} + +cmd_start() { # If already running, just display output again check_if_booted @@ -24,7 +40,7 @@ cmd_start() { FORCE_UPDATE=${1:-"--no-force"} # Always regenerate puppet configuration from environment variable source, whether changed or not. # If the current directory is not configured with an .env file, it will initialize - cmd_config $FORCE_UPDATE + cmd_lifecycle config $FORCE_UPDATE # Preflight checks # a) Check for open ports @@ -38,7 +54,7 @@ cmd_start() { # Start ${CHE_FORMAL_PRODUCT_NAME} # Note bug in docker requires relative path, not absolute path to compose file info "start" "Starting containers..." - COMPOSE_UP_COMMAND="docker_compose --file=\"${REFERENCE_CONTAINER_COMPOSE_FILE}\" -p=\"${CHE_MINI_PRODUCT_NAME}\" up -d" + COMPOSE_UP_COMMAND="docker_compose --file=\"${REFERENCE_CONTAINER_COMPOSE_FILE}\" -p=\"${CHE_COMPOSE_PROJECT_NAME}\" up -d" ## validate the compose file (quiet mode) if local_repo; then @@ -162,47 +178,9 @@ cmd_start_check_postflight() { true } -cmd_stop() { - debug $FUNCNAME - FORCE_STOP=false - if [[ "$@" == *"--force"* ]]; then - FORCE_STOP=true - fi - - if [[ ${FORCE_STOP} = "false" ]]; then - info "Waiting for graceful stop of services..." - cmd_action "graceful-stop" "$@" - fi - - # stop containers booted by docker compose - stop_containers -} - -# stop containers booted by docker compose and remove them -stop_containers() { - info "stop" "Stopping containers..." - if is_initialized; then - log "docker_compose --file=\"${REFERENCE_CONTAINER_COMPOSE_FILE}\" -p=$CHE_MINI_PRODUCT_NAME stop -t ${CHE_COMPOSE_STOP_TIMEOUT} >> \"${LOGS}\" 2>&1 || true" - docker_compose --file="${REFERENCE_CONTAINER_COMPOSE_FILE}" \ - -p=$CHE_MINI_PRODUCT_NAME stop -t ${CHE_COMPOSE_STOP_TIMEOUT} >> "${LOGS}" 2>&1 || true - info "stop" "Removing containers..." - log "docker_compose --file=\"${REFERENCE_CONTAINER_COMPOSE_FILE}\" -p=$CHE_MINI_PRODUCT_NAME rm >> \"${LOGS}\" 2>&1 || true" - docker_compose --file="${REFERENCE_CONTAINER_COMPOSE_FILE}" \ - -p=$CHE_MINI_PRODUCT_NAME rm --force >> "${LOGS}" 2>&1 || true - fi -} - -cmd_restart() { - debug $FUNCNAME - - FORCE_UPDATE=${1:-"--no-force"} - info "restart" "Restarting..." - cmd_stop ${@} - cmd_start ${FORCE_UPDATE} ${@} -} - wait_until_booted() { CURRENT_CHE_SERVER_CONTAINER_ID=$(get_server_container_id $CHE_CONTAINER_NAME) + wait_until_container_is_running 20 ${CURRENT_CHE_SERVER_CONTAINER_ID} if ! container_is_running ${CURRENT_CHE_SERVER_CONTAINER_ID}; then error "(${CHE_MINI_PRODUCT_NAME} start): Timeout waiting for ${CHE_MINI_PRODUCT_NAME} container to start." @@ -248,26 +226,27 @@ check_if_booted() { } check_containers_are_running() { - # get list of docker compose services started by this docker compose - local LIST_OF_COMPOSE_CONTAINERS=$(docker_compose --file=${REFERENCE_CONTAINER_COMPOSE_FILE} config --services) + local LIST_OF_COMPOSE_CONTAINERS=$(docker_compose --file=${REFERENCE_CONTAINER_COMPOSE_FILE} -p=$CHE_COMPOSE_PROJECT_NAME config --services) # For each service of docker-compose file, get container and then check it is running while IFS= read -r DOCKER_COMPOSE_SERVICE_NAME ; do - local CONTAINER_ID_MATCHING_SERVICE_NAME=$(docker ps -q --filter label=com.docker.compose.service=${DOCKER_COMPOSE_SERVICE_NAME}) - if [[ -z "${CONTAINER_ID_MATCHING_SERVICE_NAME}" ]]; then + local CONTAINER_ID_MATCHING_SERVICE_NAMES=$(docker ps -q --filter label=com.docker.compose.service=${DOCKER_COMPOSE_SERVICE_NAME}) + if [[ -z "${CONTAINER_ID_MATCHING_SERVICE_NAMES}" ]]; then error "Unable to find a matching container for the docker compose service named ${DOCKER_COMPOSE_SERVICE_NAME}. Check logs at ${CHE_HOST_CONFIG}/cli.log" return 2 fi - debug "Container with id ${CONTAINER_ID_MATCHING_SERVICE_NAME} is matching ${DOCKER_COMPOSE_SERVICE_NAME} service" - local IS_RUNNING_CONTAINER=$(docker inspect -f {{.State.Running}} ${CONTAINER_ID_MATCHING_SERVICE_NAME}) - debug "Running state of container ${CONTAINER_ID_MATCHING_SERVICE_NAME} is ${IS_RUNNING_CONTAINER}" - if [[ ${IS_RUNNING_CONTAINER} != "true" ]]; then - error "The container with ID ${CONTAINER_ID_MATCHING_SERVICE_NAME} of docker-compose service ${DOCKER_COMPOSE_SERVICE_NAME} is not running, aborting." - docker inspect ${CONTAINER_ID_MATCHING_SERVICE_NAME} - return 2 - fi + while IFS='\n' read -r CONTAINER_ID_MATCHING_SERVICE_NAME ; do + debug "Container with id ${CONTAINER_ID_MATCHING_SERVICE_NAME} is matching ${DOCKER_COMPOSE_SERVICE_NAME} service" + local IS_RUNNING_CONTAINER=$(docker inspect -f {{.State.Running}} ${CONTAINER_ID_MATCHING_SERVICE_NAME}) + debug "Running state of container ${CONTAINER_ID_MATCHING_SERVICE_NAME} is ${IS_RUNNING_CONTAINER}" + + if [[ ${IS_RUNNING_CONTAINER} != "true" ]]; then + error "The container with ID ${CONTAINER_ID_MATCHING_SERVICE_NAME} of docker-compose service ${DOCKER_COMPOSE_SERVICE_NAME} is not running, aborting." + docker inspect ${CONTAINER_ID_MATCHING_SERVICE_NAME} + return 2 + fi + done <<< "${CONTAINER_ID_MATCHING_SERVICE_NAMES}" done <<< "${LIST_OF_COMPOSE_CONTAINERS}" } - diff --git a/dockerfiles/base/scripts/base/commands/cmd_stop.sh b/dockerfiles/base/scripts/base/commands/cmd_stop.sh new file mode 100644 index 00000000000..7b2fe74d59c --- /dev/null +++ b/dockerfiles/base/scripts/base/commands/cmd_stop.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# Copyright (c) 2012-2016 Codenvy, S.A. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Tyler Jewell - Initial Implementation +# + +help_cmd_stop() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} restart [PARAMETERS]\n" + text "\n" + text "Stops ${CHE_MINI_PRODUCT_NAME} workspaces gracefully and then the server\n" + text "\n" + text "PARAMETERS:\n" + text " --skip:graceful Do not wait for confirmation that workspaces have stopped\n" + text "\n" +} + +pre_cmd_stop() { + true +} + +cmd_stop() { + debug $FUNCNAME + FORCE_STOP=false + if [[ "$@" == *"--skip:graceful"* ]]; then + FORCE_STOP=true + fi + + if server_is_booted $(get_server_container_id $CHE_CONTAINER_NAME); then + if [[ ${FORCE_STOP} = "false" ]]; then + info "stop" "Stopping workspaces..." + if ! $(cmd_lifecycle action "graceful-stop" "$@" >> "${LOGS}" 2>&1 || false); then + error "We encountered an error -- see cli.log" + fi + fi + # stop containers booted by docker compose + stop_containers + else + info "stop" "Server $CHE_CONTAINER_NAME on port $CHE_PORT not running..." + fi +} + +# stop containers booted by docker compose and remove them +stop_containers() { + info "stop" "Stopping containers..." + if is_initialized; then + log "docker_compose --file=\"${REFERENCE_CONTAINER_COMPOSE_FILE}\" -p=$CHE_COMPOSE_PROJECT_NAME stop -t ${CHE_COMPOSE_STOP_TIMEOUT} >> \"${LOGS}\" 2>&1 || true" + docker_compose --file="${REFERENCE_CONTAINER_COMPOSE_FILE}" \ + -p=$CHE_COMPOSE_PROJECT_NAME stop -t ${CHE_COMPOSE_STOP_TIMEOUT} >> "${LOGS}" 2>&1 || true + info "stop" "Removing containers..." + log "docker_compose --file=\"${REFERENCE_CONTAINER_COMPOSE_FILE}\" -p=$CHE_COMPOSE_PROJECT_NAME rm >> \"${LOGS}\" 2>&1 || true" + docker_compose --file="${REFERENCE_CONTAINER_COMPOSE_FILE}" \ + -p=$CHE_COMPOSE_PROJECT_NAME rm --force >> "${LOGS}" 2>&1 || true + fi +} diff --git a/dockerfiles/base/scripts/base/commands/cmd_sync.sh b/dockerfiles/base/scripts/base/commands/cmd_sync.sh index 397d3cb7503..dfced958bc5 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_sync.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_sync.sh @@ -9,21 +9,38 @@ # Tyler Jewell - Initial Implementation # -cmd_sync() { - debug $FUNCNAME +help_cmd_sync() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} sync WORKSPACE [PARAMETERS]\n" + text "\n" + text "Synchronizes a ${CHE_MINI_PRODUCT_NAME} workspace to a local path mounted to ':/sync'\n" + text "\n" + text "WORKSPACE: Accepts workspace name, ID, or namespace:ws-name\n" + text " List all workspaces with 'action list-workspaces'\n" + text "\n" + text "PARAMETERS:\n" + text " --url Location of ${CHE_MINI_PRODUCT_NAME}\n" + text " --user User name of ${CHE_MINI_PRODUCT_NAME} if accessing authenticated system\n" + text " --password Password of ${CHE_MINI_PRODUCT_NAME} if accessing authenticated system\n" + text " --unison-verbose Verbose output of unison sync\n" +} +pre_cmd_sync() { + # Not loaded as part of the init process to save on download time + load_utilities_images_if_not_done +} + +cmd_sync() { if [[ "${SYNC_MOUNT}" = "not set" ]]; then info "Welcome to $CHE_FORMAL_PRODUCT_NAME!" info "" info "We could not detect a location to do the sync." info "Volume mount a local directory to ':/sync'." info "" - info " docker run .... -v :/sync ...." + info " docker run ... -v :/sync ..." return 2; fi - # Not loaded as part of the init process to save on download time - load_utilities_images_if_not_done # Determine the mount path to do the mount info "mount" "Starting sync process to ${SYNC_MOUNT}" diff --git a/dockerfiles/base/scripts/base/commands/cmd_test.sh b/dockerfiles/base/scripts/base/commands/cmd_test.sh index 94d3ee2703a..707cb9dc495 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_test.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_test.sh @@ -6,10 +6,27 @@ # http://www.eclipse.org/legal/epl-v10.html # -cmd_test() { - debug $FUNCNAME +help_cmd_test() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} test TEST [PARAMETERS]\n" + text "\n" + text "Synchronizes a ${CHE_MINI_PRODUCT_NAME} workspace to a local path mounted to ':/sync'\n" + text "\n" + text "TESTS:\n" + text " post-flight-check Performs post-flight check to validate ${CHE_MINI_PRODUCT_NAME} install\n" + text "\n" + text "PARAMETERS:\n" + text " --quiet Display not output during test\n" + text " --user User name of ${CHE_MINI_PRODUCT_NAME} if accessing authenticated system\n" + text " --password Password of ${CHE_MINI_PRODUCT_NAME} if accessing authenticated system\n" + text " --port Define an optional port to use for the test\n" +} +pre_cmd_test() { # Not loaded as part of the init process to save on download time load_utilities_images_if_not_done +} + +cmd_test() { docker_run -it ${UTILITY_IMAGE_CHETEST} "$@" } diff --git a/dockerfiles/base/scripts/base/commands/cmd_upgrade.sh b/dockerfiles/base/scripts/base/commands/cmd_upgrade.sh index 1ef31c5ea6c..69e49ad763d 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_upgrade.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_upgrade.sh @@ -9,9 +9,21 @@ # Tyler Jewell - Initial Implementation # -cmd_upgrade() { - debug $FUNCNAME +help_cmd_upgrade() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} upgrade [PARAMETERS]\n" + text "\n" + text "Upgrades ${CHE_MINI_PRODUCT_NAME} from one version to another while protecting user workspace data" + text "\n" + text "PARAMETERS:\n" + text " --skip-backup Skip backup of user data before performing upgrade\n" +} +pre_cmd_upgrade() { + true +} + +cmd_upgrade() { CHE_IMAGE_VERSION=$(get_image_version) DO_BACKUP="true" @@ -49,13 +61,13 @@ cmd_upgrade() { if [[ "${DO_BACKUP}" == "true" ]]; then info "upgrade" "Preparing backup..." - cmd_backup + cmd_lifecycle backup else - info "upgrade" "Skipping backup." + info "upgrade" "Skipping backup" fi info "upgrade" "Reinitializing the system with your configuration..." - cmd_init --accept-license --reinit + cmd_lifecycle init --accept-license --reinit - cmd_start + cmd_lifecycle start } diff --git a/dockerfiles/base/scripts/base/commands/cmd_version.sh b/dockerfiles/base/scripts/base/commands/cmd_version.sh index af643403569..5d2cdfd2859 100644 --- a/dockerfiles/base/scripts/base/commands/cmd_version.sh +++ b/dockerfiles/base/scripts/base/commands/cmd_version.sh @@ -9,9 +9,19 @@ # Tyler Jewell - Initial Implementation # -cmd_version() { - debug $FUNCNAME +help_cmd_version() { + text "\n" + text "USAGE: ${CHE_IMAGE_FULLNAME} version\n" + text "\n" + text "List installed and available versions of ${CHE_MINI_PRODUCT_NAME}" + text "\n" +} + +pre_cmd_version() { + true +} +cmd_version() { # Do not perform any logging in this method as it is runnable before the system is bootstrap echo "" text "Your CLI version is '%s'.\n" $(get_image_version) diff --git a/dockerfiles/base/scripts/base/curl.sh b/dockerfiles/base/scripts/base/curl.sh deleted file mode 100644 index b08b6483b92..00000000000 --- a/dockerfiles/base/scripts/base/curl.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/sh -# Copyright (c) 2016 Codenvy, S.A. -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Eclipse Public License v1.0 -# which accompanies this distribution, and is available at -# http://www.eclipse.org/legal/epl-v10.html - -# Contains curl scripts - -has_curl() { - hash curl 2>/dev/null && return 0 || return 1 -} - -curl() { - - # In situations where we are performing internal pings using curl, then - # we should append the CHE_HOST as a no proxy. It seems that curl does - # not respect the NO_PROXY environment variable set on the system. - local NO_PROXY_CONFIG_FOR_CURL=("") - if [[ ! "${HTTP_PROXY}" = "" ]] || - [[ ! "${HTTPS_PROXY}" = "" ]]; then - if is_var_defined "${CHE_PRODUCT_NAME}_HOST"; then - NO_PROXY_CONFIG_FOR_CURL=("--noproxy" $(eval "echo \$${CHE_PRODUCT_NAME}_HOST")) - fi - fi - - if ! has_curl; then - log "docker run --rm --net=host appropriate/curl \"$@\"" - docker run --rm --net=host appropriate/curl ${NO_PROXY_CONFIG_FOR_CURL[@]} "$@" - else - log "$(which curl) ${NO_PROXY_CONFIG_FOR_CURL[@]} \"$@\"" - $(which curl) ${NO_PROXY_CONFIG_FOR_CURL[@]} "$@" - fi -} - -is_var_defined() -{ - if [ $# -ne 1 ] - then - echo "Expected exactly one argument: variable name as string, e.g., 'my_var'" - exit 1 - fi - eval "[ ! -z \${$1:-} ]" - return $? -} - diff --git a/dockerfiles/base/scripts/base/library.sh b/dockerfiles/base/scripts/base/library.sh index 5b321e6d791..ab6225cfe30 100644 --- a/dockerfiles/base/scripts/base/library.sh +++ b/dockerfiles/base/scripts/base/library.sh @@ -5,48 +5,30 @@ # which accompanies this distribution, and is available at # http://www.eclipse.org/legal/epl-v10.html +convert_windows_to_posix() { +# debug $FUNCNAME + echo "/"$(echo "$1" | sed 's/\\/\//g' | sed 's/://') +} -cli_execute() { - COMMAND="cmd_$1" +convert_posix_to_windows() { +# debug $FUNCNAME + # Remove leading slash + VALUE="${1:1}" - # Need to load all files in advance so commands can invoke other commands. - for COMMAND_FILE in "${SCRIPTS_CONTAINER_SOURCE_DIR}"/cmd_*.sh - do - source "${COMMAND_FILE}" - done + # Get first character (drive letter) + VALUE2="${VALUE:0:1}" - shift - eval $COMMAND "$@" -} + # Replace / with \ + VALUE3=$(echo ${VALUE} | tr '/' '\\' | sed 's/\\/\\\\/g') -cli_parse () { - COMMAND="cmd_$1" - - case $1 in - init|config|start|stop|restart|backup|restore|info|offline|destroy|download|rmi|upgrade|version|ssh|sync|action|test|compile|dir|help) - ;; - *) - error "You passed an unknown command." - usage - return 2 - ;; - esac + # Replace c\ with c:\ for drive letter + echo "$VALUE3" | sed "s/./$VALUE2:/1" } - get_boot_url() { echo "$CHE_HOST:$CHE_PORT/api/" } -# Input is a variable that would exist in the che.env file such as CHE_HOST. -# We then lookup the vlaue of this variable and return it -get_value_of_var_from_env_file() { - local LOOKUP_LOCAL=$(docker_run --env-file="${REFERENCE_CONTAINER_ENVIRONMENT_FILE}" \ - ${BOOTSTRAP_IMAGE_ALPINE} sh -c "echo \$$1") - echo $LOOKUP_LOCAL - -} - get_display_url() { # If the user has modified che.env with a custom CHE_HOST, we need to detect that here @@ -196,57 +178,6 @@ update_image_if_not_found() { fi } -update_image() { - if [ "${1}" == "--force" ]; then - shift - info "download" "Removing image $1" - log "docker rmi -f $1 >> \"${LOGS}\"" - docker rmi -f $1 >> "${LOGS}" 2>&1 || true - fi - - if [ "${1}" == "--pull" ]; then - shift - fi - - info "download" "Pulling image $1" - text "\n" - log "docker pull $1 >> \"${LOGS}\" 2>&1" - TEST="" - docker pull $1 || TEST=$? - if [ "$TEST" = "1" ]; then - error "Image $1 unavailable. Not on dockerhub or built locally." - return 2; - fi - text "\n" -} - -is_initialized() { - if [[ -d "${CHE_CONTAINER_INSTANCE}" ]] && \ - [[ -f "${CHE_CONTAINER_INSTANCE}"/$CHE_VERSION_FILE ]] && \ - [[ -f "${REFERENCE_CONTAINER_ENVIRONMENT_FILE}" ]]; then - return 0 - else - return 1 - fi -} - -is_configured() { - if [[ -d "${CHE_CONTAINER_INSTANCE}/config" ]] && \ - [[ -f "${CHE_CONTAINER_INSTANCE}/${CHE_VERSION_FILE}" ]]; then - return 0 - else - return 1 - fi -} - -has_version_registry() { - if [ -d /version/$1 ]; then - return 0; - else - return 1; - fi -} - list_versions(){ # List all subdirectories and then print only the file name for version in /version/* ; do @@ -254,22 +185,6 @@ list_versions(){ done } -version_error(){ - text "\nWe could not find version '$1'. Available versions:\n" - list_versions - text "\nSet CHE_VERSION= and rerun.\n\n" -} - -### define variables for all image name in the given list -set_variables_images_list() { - IFS=$'\n' - for SINGLE_IMAGE in $1; do - log "eval $SINGLE_IMAGE" - eval $SINGLE_IMAGE - done - -} - ### Returns the list of ${CHE_FORMAL_PRODUCT_NAME} images for a particular version of ${CHE_FORMAL_PRODUCT_NAME} ### Sets the images as environment variables after loading from file get_image_manifest() { @@ -291,211 +206,6 @@ get_image_manifest() { } -get_installed_version() { - if ! is_initialized; then - echo "" - else - cat "${CHE_CONTAINER_INSTANCE}"/$CHE_VERSION_FILE - fi -} - -get_image_version() { - echo "$CHE_IMAGE_VERSION" -} - -less_than() { - for (( i=0; i<${#1}; i++ )); do - if [[ ${1:$i:1} != ${2:$i:1} ]]; then - if [ ${1:$i:1} -lt ${2:$i:1} ]; then - return 0 - else - return 1 - fi - fi - done - return 1 -} - -# Check if a version is < than another version (provide like for example : version_lt 5.0 5.1) -version_lt() { - test "$(printf '%s\n' "$@" | sort -V | head -n 1)" == "$1"; - return $?; -} - -# return true if the provided version is an intermediate version (like a Milestone or a Release Candidate) -is_intermediate_version() { - if [[ "$1" =~ .*-M.* ]]; then - return 0 - fi - if [[ "$1" =~ .*-RC.* ]]; then - return 0 - fi - return 1 -} - -compare_cli_version_to_installed_version() { - IMAGE_VERSION=$(get_image_version) - INSTALLED_VERSION=$(get_installed_version) - - # add -ZZ suffix if not intermediate version - # So for example 5.0.0 is transformed into 5.0.0-ZZ and is greater than 5.0.0-M8 - IMAGE_VERSION_SUFFIXED=${IMAGE_VERSION} - INSTALLED_VERSION_SUFFIXED=${INSTALLED_VERSION} - if ! is_intermediate_version ${IMAGE_VERSION}; then - IMAGE_VERSION_SUFFIXED="${IMAGE_VERSION}-ZZ" - fi - if ! is_intermediate_version ${INSTALLED_VERSION}; then - INSTALLED_VERSION_SUFFIXED="${INSTALLED_VERSION}-ZZ" - fi - - if [[ "$INSTALLED_VERSION" = "$IMAGE_VERSION" ]]; then - echo "match" - elif [ "$INSTALLED_VERSION" = "nightly" ] || - [ "$IMAGE_VERSION" = "nightly" ]; then - echo "nightly" - elif version_lt $INSTALLED_VERSION_SUFFIXED $IMAGE_VERSION_SUFFIXED; then - echo "install-less-cli" - else - echo "cli-less-install" - fi -} - -verify_version_compatibility() { - - ## If ! is_initialized, then the system hasn't been installed - ## First, compare the CLI image version to what version was initialized in /config/*.ver.donotmodify - ## - If they match, good - ## - If they don't match and one is nightly, fail - ## - If they don't match, then if CLI is older fail with message to get proper CLI - ## - If they don't match, then if CLLI is newer fail with message to run upgrade first - CHE_IMAGE_VERSION=$(get_image_version) - - # Only check for newer versions if not in offline mode and not nightly. - if ! is_offline && ! is_nightly; then - NEWER=$(compare_versions $CHE_IMAGE_VERSION) - - if [[ "${NEWER}" != "" ]]; then - warning "Newer version '$NEWER' available" - fi - fi - - if is_initialized; then - COMPARE_CLI_ENV=$(compare_cli_version_to_installed_version) - INSTALLED_VERSION=$(get_installed_version) - case "${COMPARE_CLI_ENV}" in - "match") - ;; - "nightly") - error "" - error "Your CLI version '${CHE_IMAGE_FULLNAME}' does not match your installed version '$INSTALLED_VERSION' in ${DATA_MOUNT}." - error "" - error "The 'nightly' CLI is only compatible with 'nightly' installed versions." - error "You may use 'nightly' with a separate ${CHE_FORMAL_PRODUCT_NAME} installation by providing a different ':/data' volume mount." - error "You may not '${CHE_MINI_PRODUCT_NAME} upgrade' from 'nightly' to a numbered (tagged) version." - error "" - error "Run the CLI as '${CHE_IMAGE_NAME}:' to install a tagged version." - return 2 - ;; - "install-less-cli") - error "" - error "Your CLI version '${CHE_IMAGE_FULLNAME}' is newer than your installed version '$INSTALLED_VERSION'." - error "" - error "Run '${CHE_IMAGE_FULLNAME} upgrade' to migrate your installation to '$CHE_IMAGE_VERSION'." - error "Or, run the CLI with '${CHE_IMAGE_NAME}:$INSTALLED_VERSION' to match the CLI with your installed version." - return 2 - ;; - "cli-less-install") - error "" - error "Your CLI version '${CHE_IMAGE_FULLNAME}' is older than your installed version '$INSTALLED_VERSION'." - error "" - error "You cannot use an older CLI with a newer installation." - error "" - error "Run the CLI with '${CHE_IMAGE_NAME}:$INSTALLED_VERSION' to match the CLI with your existing installed version." - return 2 - ;; - esac - fi -} - -is_nightly() { - if [[ $(get_image_version) = "nightly" ]]; then - return 0 - else - return 1 - fi -} - -verify_nightly_accuracy() { - # Per request of the engineers, check to see if the locally cached nightly version is older - # than the one stored on DockerHub. - if is_nightly; then - - if ! is_fast && ! skip_nightly; then - local CURRENT_DIGEST=$(docker images -q --no-trunc --digests ${CHE_IMAGE_FULLNAME}) - - update_image $CHE_IMAGE_FULLNAME - - local NEW_DIGEST=$(docker images -q --no-trunc --digests ${CHE_IMAGE_FULLNAME}) - - if [[ "${CURRENT_DIGEST}" != "${NEW_DIGEST}" ]]; then - warning "Pulled new 'nightly' image - please rerun CLI" - return 2; - fi - fi - fi -} - -verify_version_upgrade_compatibility() { - ## Two levels of checks - ## First, compare the CLI image version to what the admin has configured in /config/.env file - ## - If they match, nothing to upgrade - ## - If they don't match and one is nightly, fail upgrade is not supported for nightly - ## - If they don't match, then if CLI is older fail with message that we do not support downgrade - ## - If they don't match, then if CLI is newer then good - CHE_IMAGE_VERSION=$(get_image_version) - - if ! is_initialized || ! is_configured; then - info "upgrade" "$CHE_MINI_PRODUCT_NAME is not installed or configured. Nothing to upgrade." - return 2 - fi - - if is_initialized; then - COMPARE_CLI_ENV=$(compare_cli_version_to_installed_version) - CONFIGURED_VERSION=$(get_installed_version) - - case "${COMPARE_CLI_ENV}" in - "match") - error "" - error "Your CLI version '${CHE_IMAGE_FULLNAME}' is identical to your installed version '$CONFIGURED_VERSION'." - error "" - error "Run '${CHE_IMAGE_NAME}: upgrade' with a newer version to upgrade." - error "View available versions with '$CHE_FORMAL_PRODUCT_NAME version'." - return 2 - ;; - "nightly") - error "" - error "Your CLI version '${CHE_IMAGE_FULLNAME}' or installed version '$CONFIGURED_VERSION' is nightly." - error "" - error "You may not '${CHE_IMAGE_NAME} upgrade' from 'nightly' to a numbered (tagged) version." - error "You can 'docker pull ${CHE_IMAGE_FULLNAME}' to get a newer nightly version." - return 2 - ;; - "install-less-cli") - ;; - "cli-less-install") - error "" - error "Your CLI version '${CHE_IMAGE_FULLNAME}' is older than your installed version '$CONFIGURED_VERSION'." - error "" - error "You cannot use '${CHE_IMAGE_NAME} upgrade' to downgrade versions." - error "" - error "Run '${CHE_IMAGE_NAME}: upgrade' with a newer version to upgrade." - error "View available versions with '${CHE_IMAGE_NAME} version'." - return 2 - ;; - esac - fi -} - # Usage: # confirm_operation [--force|--no-force] confirm_operation() { @@ -593,7 +303,7 @@ check_all_ports(){ HTTPD_PORT_STRING+=" -p $PORT" done - EXECUTION_STRING="docker run -it --rm ${DOCKER_PORT_STRING} ${UTILITY_IMAGE_ALPINE} \ + EXECUTION_STRING="docker run -it --rm ${DOCKER_PORT_STRING} ${BOOTSTRAP_IMAGE_ALPINE} \ sh -c \"echo hi\" > /dev/null 2>&1" eval ${EXECUTION_STRING} NETSTAT_EXIT=$? @@ -627,3 +337,130 @@ find_and_print_ports_as_notok() { error "Ports required to run $CHE_MINI_PRODUCT_NAME are used by another program." return 2; } + +container_exist_by_name(){ + docker inspect ${1} > /dev/null 2>&1 + if [ "$?" == "0" ]; then + return 0 + else + return 1 + fi +} + +get_server_container_id() { + log "docker inspect -f '{{.Id}}' ${1}" + docker inspect -f '{{.Id}}' ${1} 2>&1 || false +} + +container_is_running() { + if [ "$(docker ps -qa -f "status=running" -f "id=${1}" | wc -l)" -eq 0 ]; then + return 1 + else + return 0 + fi +} + +wait_until_container_is_running() { + CONTAINER_START_TIMEOUT=${1} + + ELAPSED=0 + until container_is_running ${2} || [ ${ELAPSED} -eq "${CONTAINER_START_TIMEOUT}" ]; do + log "sleep 1" + sleep 1 + ELAPSED=$((ELAPSED+1)) + done +} + +has_compose() { + hash docker-compose 2>/dev/null && return 0 || return 1 +} + +docker_compose() { +# debug $FUNCNAME + + if has_compose; then + docker-compose "$@" + else + docker_run -v "${CHE_HOST_INSTANCE}":"${CHE_CONTAINER_INSTANCE}" \ + $IMAGE_COMPOSE "$@" + fi +} + +start_test_server() { + export AGENT_INTERNAL_PORT=80 + export AGENT_EXTERNAL_PORT=32768 + + # Start mini httpd server to run simulated tests + docker run -d -p $AGENT_EXTERNAL_PORT:$AGENT_INTERNAL_PORT --name fakeagent \ + ${BOOTSTRAP_IMAGE_ALPINE} httpd -f -p $AGENT_INTERNAL_PORT -h /etc/ >> "${LOGS}" + + export AGENT_INTERNAL_IP=$(docker inspect --format='{{.NetworkSettings.IPAddress}}' fakeagent) + export AGENT_EXTERNAL_IP=$CHE_HOST +} + +stop_test_server() { + # Remove httpd server + docker rm -f fakeagent >> "${LOGS}" +} + +test1() { + HTTP_CODE=$(curl -I localhost:${AGENT_EXTERNAL_PORT}/alpine-release \ + -s -o "${LOGS}" --connect-timeout 5 \ + --write-out '%{http_code}') || echo "28" >> "${LOGS}" + + if check_http_code $HTTP_CODE; then + return 0 + else + return 1 + fi +} + +test2() { + HTTP_CODE=$(curl -I ${AGENT_EXTERNAL_IP}:${AGENT_EXTERNAL_PORT}/alpine-release \ + -s -o "${LOGS}" --connect-timeout 5 \ + --write-out '%{http_code}') || echo "28" >> "${LOGS}" + + if check_http_code $HTTP_CODE; then + return 0 + else + return 1 + fi +} + +test3() { + HTTP_CODE=$(docker_run --name fakeserver \ + --entrypoint=curl \ + $(eval "echo \${IMAGE_${CHE_PRODUCT_NAME}}") \ + -I ${AGENT_EXTERNAL_IP}:${AGENT_EXTERNAL_PORT}/alpine-release \ + -s -o "${LOGS}" \ + --write-out '%{http_code}') + + if check_http_code $HTTP_CODE; then + return 0 + else + return 1 + fi +} + +test4() { + HTTP_CODE=$(docker_run --name fakeserver \ + --entrypoint=curl \ + $(eval "echo \${IMAGE_${CHE_PRODUCT_NAME}}") \ + -I ${AGENT_INTERNAL_IP}:${AGENT_INTERNAL_PORT}/alpine-release \ + -s -o "${LOGS}" \ + --write-out '%{http_code}') + + if check_http_code $HTTP_CODE; then + return 0 + else + return 1 + fi +} + +check_http_code() { + if [ "${1}" = "200" ]; then + return 0 + else + return 1 + fi +} diff --git a/dockerfiles/base/scripts/base/paths.sh b/dockerfiles/base/scripts/base/paths.sh deleted file mode 100644 index 2197dc30b3d..00000000000 --- a/dockerfiles/base/scripts/base/paths.sh +++ /dev/null @@ -1,85 +0,0 @@ -#!/bin/sh -# Copyright (c) 2016 Codenvy, S.A. -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Eclipse Public License v1.0 -# which accompanies this distribution, and is available at -# http://www.eclipse.org/legal/epl-v10.html - -# Contains path utilities - -check_host_volume_mount() { - if is_boot2docker; then - warning "Boot2docker detected - ensure :/data is mounted to %userprofile%" - fi - - if file_system_writable "${CHE_CONTAINER_ROOT}/test"; then - delete_file_system_test "${CHE_CONTAINER_ROOT}/test" - else - error "Unable to write files to your host" - error "Have you enabled Docker to allow mounting host directories?" - error "Did you give our CLI rights to create files on your host?" - return 2; - fi -} - -file_system_writable() { - echo 'test' > "${1}" - - if [[ -f "${1}" ]]; then - return 0 - else - return 1 - fi -} - -delete_file_system_test() { - rm -rf $1 > /dev/null 2>&1 -} - -get_mount_path() { -# debug $FUNCNAME - FULL_PATH=$(get_full_path "${1}") - POSIX_PATH=$(convert_windows_to_posix "${FULL_PATH}") - CLEAN_PATH=$(get_clean_path "${POSIX_PATH}") - echo $CLEAN_PATH -} - -get_full_path() { -# debug $FUNCNAME - # create full directory path - echo "$(cd "$(dirname "${1}")"; pwd)/$(basename "$1")" -} - -convert_windows_to_posix() { -# debug $FUNCNAME - echo "/"$(echo "$1" | sed 's/\\/\//g' | sed 's/://') -} - -convert_posix_to_windows() { -# debug $FUNCNAME - # Remove leading slash - VALUE="${1:1}" - - # Get first character (drive letter) - VALUE2="${VALUE:0:1}" - - # Replace / with \ - VALUE3=$(echo ${VALUE} | tr '/' '\\' | sed 's/\\/\\\\/g') - - # Replace c\ with c:\ for drive letter - echo "$VALUE3" | sed "s/./$VALUE2:/1" -} - -get_clean_path() { -# debug $FUNCNAME - INPUT_PATH=$1 - # \some\path => /some/path - OUTPUT_PATH=$(echo ${INPUT_PATH} | tr '\\' '/') - # /somepath/ => /somepath - OUTPUT_PATH=${OUTPUT_PATH%/} - # /some//path => /some/path - OUTPUT_PATH=$(echo ${OUTPUT_PATH} | tr -s '/') - # "/some/path" => /some/path - OUTPUT_PATH=${OUTPUT_PATH//\"} - echo ${OUTPUT_PATH} -} \ No newline at end of file diff --git a/dockerfiles/base/scripts/base/startup.sh b/dockerfiles/base/scripts/base/startup.sh index 30677c1952e..a076d8a47a3 100644 --- a/dockerfiles/base/scripts/base/startup.sh +++ b/dockerfiles/base/scripts/base/startup.sh @@ -5,7 +5,6 @@ # which accompanies this distribution, and is available at # http://www.eclipse.org/legal/epl-v10.html - # Check pre/post functions are there or not declare -f pre_init > /dev/null if [ "$?" == "1" ]; then @@ -13,6 +12,7 @@ if [ "$?" == "1" ]; then : } fi + declare -f post_init > /dev/null if [ "$?" == "1" ]; then post_init() { @@ -20,10 +20,10 @@ if [ "$?" == "1" ]; then } fi -source /scripts/base/startup_funcs.sh +source /scripts/base/startup_01_init.sh # See: https://sipb.mit.edu/doc/safe-shell/ set -e set -u -trap "cleanup" INT TERM EXIT \ No newline at end of file +trap "cleanup" INT TERM EXIT diff --git a/dockerfiles/base/scripts/base/startup_funcs.sh b/dockerfiles/base/scripts/base/startup_01_init.sh similarity index 63% rename from dockerfiles/base/scripts/base/startup_funcs.sh rename to dockerfiles/base/scripts/base/startup_01_init.sh index 33e56e444b4..4bcde858b22 100644 --- a/dockerfiles/base/scripts/base/startup_funcs.sh +++ b/dockerfiles/base/scripts/base/startup_01_init.sh @@ -48,7 +48,8 @@ GLOBAL COMMAND OPTIONS: --fast Skips networking, version, nightly and preflight checks --offline Runs CLI in offline mode, loading images from disk --debug Enable debugging of ${CHE_MINI_PRODUCT_NAME} server - --trace Activates trace output for debugging CLI${ADDITIONAL_GLOBAL_OPTIONS} + --trace Activates trace output for debugging CLI${ADDITIONAL_GLOBAL_OPTIONS} + --help Get help for a command " } @@ -60,6 +61,14 @@ init_constants() { BOLD='\033[1m' UNDERLINE='\033[4m' NC='\033[0m' + + # CLI DEVELOPERS - ONLY INCREMENT THIS CHANGE IF MODIFYING SECTIONS THAT AFFECT LOADING + # BEFORE :/REPO IS VOLUME MOUNTED. CLI ASSEMBLIES WILL FAIL UNTIL THEY + # ARE RECOMPILED WITH MATCHING VERSION. + CHE_BASE_API_VERSION=1 +} + +init_global_vars() { LOG_INITIALIZED=false FAST_BOOT=false CHE_DEBUG=false @@ -69,6 +78,7 @@ init_constants() { CHE_SKIP_NIGHTLY=false CHE_SKIP_NETWORK=false CHE_SKIP_PULL=false + CHE_COMMAND_HELP=false DEFAULT_CHE_PRODUCT_NAME="CHE" CHE_PRODUCT_NAME=${CHE_PRODUCT_NAME:-${DEFAULT_CHE_PRODUCT_NAME}} @@ -109,6 +119,9 @@ init_constants() { DEFAULT_CHE_SCRIPTS_CONTAINER_SOURCE_DIR="/repo/dockerfiles/cli/scripts" CHE_SCRIPTS_CONTAINER_SOURCE_DIR=${CHE_SCRIPTS_CONTAINER_SOURCE_DIR:-${DEFAULT_CHE_SCRIPTS_CONTAINER_SOURCE_DIR}} + DEFAULT_CHE_BASE_SCRIPTS_CONTAINER_SOURCE_DIR="/scripts/base" + CHE_BASE_SCRIPTS_CONTAINER_SOURCE_DIR=${CHE_BASE_SCRIPTS_CONTAINER_SOURCE_DIR:-${DEFAULT_CHE_BASE_SCRIPTS_CONTAINER_SOURCE_DIR}} + DEFAULT_CHE_LICENSE_URL="https://www.eclipse.org/legal/epl-v10.html" CHE_LICENSE_URL=${CHE_LICENSE_URL:-${DEFAULT_CHE_LICENSE_URL}} @@ -141,245 +154,46 @@ init_constants() { DEFAULT_CHE_LICENSE=false CHE_LICENSE=${CHE_LICENSE:-${DEFAULT_CHE_LICENSE}} - # Replace all of these with digests - UTILITY_IMAGE_ALPINE="alpine:3.4" - UTILITY_IMAGE_CHEIP="eclipse/che-ip:nightly" - UTILITY_IMAGE_CHEACTION="eclipse/che-action:nightly" - UTILITY_IMAGE_CHEDIR="eclipse/che-dir:nightly" - UTILITY_IMAGE_CHETEST="eclipse/che-test:nightly" - UTILITY_IMAGE_CHEMOUNT="eclipse/che-mount:nightly" -} - - -# Sends arguments as a text to CLI log file -# Usage: -# log [other arguments] -log() { - if [[ "$LOG_INITIALIZED" = "true" ]]; then - if is_log; then - echo "$@" >> "${LOGS}" + if [[ "${CHE_CONTAINER_NAME}" = "${CHE_MINI_PRODUCT_NAME}" ]]; then + if [[ "${CHE_PORT}" != "${DEFAULT_CHE_PORT}" ]]; then + CHE_CONTAINER_NAME="${CHE_CONTAINER_PREFIX}-${CHE_PORT}" + else + CHE_CONTAINER_NAME="${CHE_CONTAINER_PREFIX}" fi fi + + DEFAULT_CHE_COMPOSE_PROJECT_NAME="${CHE_CONTAINER_NAME}" + CHE_COMPOSE_PROJECT_NAME="${CHE_COMPOSE_PROJECT_NAME:-${DEFAULT_CHE_COMPOSE_PROJECT_NAME}}" } usage() { # debug $FUNCNAME init_usage printf "%s" "${USAGE}" - return 1; -} - -warning() { - if is_warning; then - printf "${YELLOW}WARN:${NC} %s\n" "${1}" - fi - log $(printf "WARN: %s\n" "${1}") -} - -info() { - if [ -z ${2+x} ]; then - PRINT_COMMAND="" - PRINT_STATEMENT=$1 - else - PRINT_COMMAND="($CHE_MINI_PRODUCT_NAME $1): " - PRINT_STATEMENT=$2 - fi - if is_info; then - printf "${GREEN}INFO:${NC} %b%b\n" \ - "${PRINT_COMMAND}" \ - "${PRINT_STATEMENT}" - fi - log $(printf "INFO: %b %b\n" \ - "${PRINT_COMMAND}" \ - "${PRINT_STATEMENT}") -} - -debug() { - if is_debug; then - printf "\n${BLUE}DEBUG:${NC} %s" "${1}" - fi - log $(printf "\nDEBUG: %s" "${1}") -} - -error() { - printf "${RED}ERROR:${NC} %s\n" "${1}" - log $(printf "ERROR: %s\n" "${1}") -} - -# Prints message without changes -# Usage: has the same syntax as printf command -text() { - printf "$@" - log $(printf "$@") -} - -## TODO use that for all native calls to improve logging for support purposes -# Executes command with 'eval' command. -# Also logs what is being executed and stdout/stderr -# Usage: -# cli_eval -# Examples: -# cli_eval "$(which curl) http://localhost:80/api/" -cli_eval() { - log "$@" - tmpfile=$(mktemp) - if eval "$@" &>"${tmpfile}"; then - # Execution succeeded - cat "${tmpfile}" >> "${LOGS}" - cat "${tmpfile}" - rm "${tmpfile}" - else - # Execution failed - cat "${tmpfile}" >> "${LOGS}" - cat "${tmpfile}" - rm "${tmpfile}" - fail - fi -} - -# Executes command with 'eval' command and suppress stdout/stderr. -# Also logs what is being executed and stdout+stderr -# Usage: -# cli_silent_eval -# Examples: -# cli_silent_eval "$(which curl) http://localhost:80/api/" -cli_silent_eval() { - log "$@" - eval "$@" >> "${LOGS}" 2>&1 -} - -is_log() { - if [ "${CHE_CLI_LOG}" = "true" ]; then - return 0 - else - return 1 - fi } -is_warning() { - if [ "${CHE_CLI_WARN}" = "true" ]; then - return 0 - else - return 1 - fi -} - -is_info() { - if [ "${CHE_CLI_INFO}" = "true" ]; then - return 0 - else - return 1 - fi -} - -is_debug() { - if [ "${CHE_CLI_DEBUG}" = "true" ]; then - return 0 - else - return 1 - fi -} - -debug_server() { - if [ "${CHE_DEBUG}" = "true" ]; then - return 0 - else - return 1 +init_cli_version_check() { + if [[ $CHE_BASE_API_VERSION != $CHE_CLI_API_VERSION ]]; then + printf "CLI base ($CHE_BASE_API_VERSION) does not match CLI ($CHE_CLI_API_VERSION) version.\n" + printf "Recompile the CLI with the latest version of the CLI base.\n" + return 1; fi } -is_fast() { - if [ "${FAST_BOOT}" = "true" ]; then - return 0 - else - return 1 - fi -} - -is_offline() { - if [ "${CHE_OFFLINE}" = "true" ]; then - return 0 - else - return 1 - fi -} - -is_trace() { - if [ "${CHE_TRACE}" = "true" ]; then - return 0 - else - return 1 - fi -} - -skip_preflight() { - if [ "${CHE_SKIP_PREFLIGHT}" = "true" ]; then - return 0 - else - return 1 - fi -} - -skip_postflight() { - if [ "${CHE_SKIP_POSTFLIGHT}" = "true" ]; then - return 0 - else - return 1 - fi -} - -skip_nightly() { - if [ "${CHE_SKIP_NIGHTLY}" = "true" ]; then - return 0 - else - return 1 - fi -} - -skip_network() { - if [ "${CHE_SKIP_NETWORK}" = "true" ]; then - return 0 - else - return 1 - fi -} - -skip_pull() { - if [ "${CHE_SKIP_PULL}" = "true" ]; then - return 0 - else - return 1 - fi -} - -init_logging() { - # Initialize CLI folder - CLI_DIR=$CHE_CONTAINER_ROOT - test -d "${CLI_DIR}" || mkdir -p "${CLI_DIR}" - - # Ensure logs folder exists - LOGS="${CLI_DIR}/cli.log" - LOG_INITIALIZED=true - - # Log date of CLI execution - log "$(date)" -} - - -init() { - init_constants - +init_usage_check() { # If there are no parameters, immediately display usage + if [[ $# == 0 ]]; then - usage; + usage + return 1 fi if [[ "$@" == *"--fast"* ]]; then - FAST_BOOT=true + FAST_BOOT=true fi if [[ "$@" == *"--debug"* ]]; then - CHE_DEBUG=true + CHE_DEBUG=true fi if [[ "$@" == *"--offline"* ]]; then @@ -411,12 +225,18 @@ init() { CHE_SKIP_PULL=true fi - SCRIPTS_BASE_CONTAINER_SOURCE_DIR="/scripts/base" - # add helper scripts - for HELPER_FILE in "${SCRIPTS_BASE_CONTAINER_SOURCE_DIR}"/*.sh - do - source "${HELPER_FILE}" - done + if [[ "$@" == *"--help"* ]]; then + CHE_COMMAND_HELP=true + fi +} + +init() { + init_constants + init_global_vars + init_cli_version_check + init_usage_check "$@" + + source "${CHE_BASE_SCRIPTS_CONTAINER_SOURCE_DIR}"/startup_02_pre_docker.sh # Make sure Docker is working and we have /var/run/docker.sock mounted or valid DOCKER_HOST check_docker "$@" @@ -434,25 +254,24 @@ init() { init_logging "$@" SCRIPTS_CONTAINER_SOURCE_DIR="" - if $CHE_LOCAL_REPO; then + SCRIPTS_BASE_CONTAINER_SOURCE_DIR="" + if local_repo; then # Use the CLI that is inside the repository. SCRIPTS_CONTAINER_SOURCE_DIR=${CHE_SCRIPTS_CONTAINER_SOURCE_DIR} + + if [[ -d "/repo/dockerfiles/base/scripts/base" ]]; then + SCRIPTS_BASE_CONTAINER_SOURCE_DIR="/repo/dockerfiles/base/scripts/base" + else + SCRIPTS_BASE_CONTAINER_SOURCE_DIR=${CHE_BASE_SCRIPTS_CONTAINER_SOURCE_DIR} + fi + else # Use the CLI that is inside the container. SCRIPTS_CONTAINER_SOURCE_DIR="/scripts" + SCRIPTS_BASE_CONTAINER_SOURCE_DIR=${CHE_BASE_SCRIPTS_CONTAINER_SOURCE_DIR} fi - # Primary source directory - source "${SCRIPTS_BASE_CONTAINER_SOURCE_DIR}"/library.sh - - # add base commands - for BASECOMMAND_FILE in "${SCRIPTS_BASE_CONTAINER_SOURCE_DIR}"/commands/*.sh - do - source "${BASECOMMAND_FILE}" - done - - # sources post_init functions that can only be loaded after other libraries - source "${SCRIPTS_CONTAINER_SOURCE_DIR}"/cli.sh + source "${SCRIPTS_BASE_CONTAINER_SOURCE_DIR}"/startup_03_pre_networking.sh # If offline mode, then load dependent images from disk and populate the local Docker cache. # If not in offline mode, verify that we have access to DockerHub. @@ -464,51 +283,6 @@ init() { grab_initial_images } -cli_pre_init() { - : -} - -cli_post_init() { - : -} - -cli_init() { - CHE_HOST=$(eval "echo \$${CHE_PRODUCT_NAME}_HOST") - CHE_PORT=$(eval "echo \$${CHE_PRODUCT_NAME}_PORT") - - if [[ "$(eval "echo \$${CHE_PRODUCT_NAME}_HOST")" = "" ]]; then - info "Welcome to $CHE_FORMAL_PRODUCT_NAME!" - info "" - info "We did not auto-detect a valid HOST or IP address." - info "Pass ${CHE_PRODUCT_NAME}_HOST with your hostname or IP address." - info "" - info "Rerun the CLI:" - info " docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock" - info " -v :${CHE_CONTAINER_ROOT}" - info " -e ${CHE_PRODUCT_NAME}_HOST=" - info " $CHE_IMAGE_FULLNAME $*" - return 2; - fi - - if is_initialized; then - CHE_HOST_LOCAL=$(get_value_of_var_from_env_file ${CHE_PRODUCT_NAME}_HOST) - if [[ "${CHE_HOST}" != "${CHE_HOST_LOCAL}" ]]; then - warning "${CHE_PRODUCT_NAME}_HOST (${CHE_HOST}) overridden by ${CHE_ENVIRONMENT_FILE} (${CHE_HOST_LOCAL})" - fi - fi - - # Special function to perform special behaviors if you are running nightly version - verify_nightly_accuracy - - # Do not perform a version compatibility check if running upgrade command. - # The upgrade command has its own internal checks for version compatibility. - if [[ "$@" == *"upgrade"* ]]; then - verify_version_upgrade_compatibility - elif ! is_fast; then - verify_version_compatibility - fi -} - cleanup() { RETURN_CODE=$? @@ -525,6 +299,10 @@ start() { # pre_init is unique to each CLI assembly. This can be called before # networking is established. + + # Each CLI assembly must provide this cli.sh - loads overridden functions and variables for the CLI + # Hard code this location + source "/scripts/pre_init.sh" pre_init # Bootstrap networking, docker, logging, and ability to load cli.sh and library.sh @@ -541,19 +319,34 @@ start() { set -- "${@/\-\-skip\:nightly/}" set -- "${@/\-\-skip\:network/}" set -- "${@/\-\-skip\:pull/}" - + set -- "${@/\-\-help/}" + + # Each CLI assembly must provide this cli.sh - loads overridden functions and variables for the CLI + source "${SCRIPTS_CONTAINER_SOURCE_DIR}"/post_init.sh + # The post_init method is unique to each assembly. This method must be provided by # a custom CLI assembly in their container and can set global variables which are # specific to that implementation of the CLI. Place initialization functions that # require networking here. post_init - + # Begin product-specific CLI calls info "cli" "$CHE_VERSION - using docker ${DOCKER_SERVER_VERSION} / $(get_docker_install_type)" + source "${SCRIPTS_BASE_CONTAINER_SOURCE_DIR}"/startup_04_pre_cli_init.sh + cli_pre_init cli_init "$@" cli_post_init + + source "${SCRIPTS_BASE_CONTAINER_SOURCE_DIR}"/startup_05_pre_exec.sh + + # Loads the library and associated dependencies + cli_load "$@" + + # Parses the command list for validity cli_parse "$@" + + # Executes command lifecycle cli_execute "$@" } diff --git a/dockerfiles/base/scripts/base/docker.sh b/dockerfiles/base/scripts/base/startup_02_pre_docker.sh similarity index 65% rename from dockerfiles/base/scripts/base/docker.sh rename to dockerfiles/base/scripts/base/startup_02_pre_docker.sh index 62e6d625dd7..64a573174f6 100644 --- a/dockerfiles/base/scripts/base/docker.sh +++ b/dockerfiles/base/scripts/base/startup_02_pre_docker.sh @@ -5,185 +5,198 @@ # which accompanies this distribution, and is available at # http://www.eclipse.org/legal/epl-v10.html -# Contains docker scripts - -has_docker() { - hash docker 2>/dev/null && return 0 || return 1 +# Sends arguments as a text to CLI log file +# Usage: +# log [other arguments] +log() { + if [[ "$LOG_INITIALIZED" = "true" ]]; then + if is_log; then + echo "$@" >> "${LOGS}" + fi + fi } -has_compose() { - hash docker-compose 2>/dev/null && return 0 || return 1 +warning() { + if is_warning; then + printf "${YELLOW}WARN:${NC} %s\n" "${1}" + fi + log $(printf "WARN: %s\n" "${1}") } -get_container_folder() { - THIS_CONTAINER_ID=$(get_this_container_id) - FOLDER=$(get_container_host_bind_folder "$1" $THIS_CONTAINER_ID) - echo "${FOLDER:=not set}" +info() { + if [ -z ${2+x} ]; then + PRINT_COMMAND="" + PRINT_STATEMENT=$1 + else + PRINT_COMMAND="($CHE_MINI_PRODUCT_NAME $1): " + PRINT_STATEMENT=$2 + fi + if is_info; then + printf "${GREEN}INFO:${NC} %b%b\n" \ + "${PRINT_COMMAND}" \ + "${PRINT_STATEMENT}" + fi + log $(printf "INFO: %b %b\n" \ + "${PRINT_COMMAND}" \ + "${PRINT_STATEMENT}") } -get_this_container_id() { - hostname +debug() { + if is_debug; then + printf "\n${BLUE}DEBUG:${NC} %s" "${1}" + fi + log $(printf "\nDEBUG: %s" "${1}") } -get_container_host_bind_folder() { - # BINDS in the format of var/run/docker.sock:/var/run/docker.sock :${CHE_CONTAINER_ROOT} - BINDS=$(docker inspect --format="{{.HostConfig.Binds}}" "${2}" | cut -d '[' -f 2 | cut -d ']' -f 1) - - # Remove /var/run/docker.sock:/var/run/docker.sock - #VALUE=${BINDS/\/var\/run\/docker\.sock\:\/var\/run\/docker\.sock/} +error() { + printf "${RED}ERROR:${NC} %s\n" "${1}" + log $(printf "ERROR: %s\n" "${1}") +} - # Remove leading and trailing spaces - VALUE2=$(echo "${BINDS}" | xargs) +# Prints message without changes +# Usage: has the same syntax as printf command +text() { + printf "$@" + log $(printf "$@") +} - MOUNT="" - IFS=$' ' - for SINGLE_BIND in $VALUE2; do - case $SINGLE_BIND in +## TODO use that for all native calls to improve logging for support purposes +# Executes command with 'eval' command. +# Also logs what is being executed and stdout/stderr +# Usage: +# cli_eval +# Examples: +# cli_eval "$(which curl) http://localhost:80/api/" +cli_eval() { + log "$@" + tmpfile=$(mktemp) + if eval "$@" &>"${tmpfile}"; then + # Execution succeeded + cat "${tmpfile}" >> "${LOGS}" + cat "${tmpfile}" + rm "${tmpfile}" + else + # Execution failed + cat "${tmpfile}" >> "${LOGS}" + cat "${tmpfile}" + rm "${tmpfile}" + fail + fi +} - # Fix for CHE-3863 - in case there is :Z after the mount for SELinux, add * - *$1*) - MOUNT="${MOUNT} ${SINGLE_BIND}" - echo "${MOUNT}" | cut -f1 -d":" | xargs - ;; - *) - # Super ugly - since we parse by space, if the next parameter is not a colon, then - # we know that next parameter is second part of a directory with a space in it. - if [[ ${SINGLE_BIND} != *":"* ]]; then - MOUNT="${MOUNT} ${SINGLE_BIND}" - else - MOUNT="" - fi - ;; - esac - done +# Executes command with 'eval' command and suppress stdout/stderr. +# Also logs what is being executed and stdout+stderr +# Usage: +# cli_silent_eval +# Examples: +# cli_silent_eval "$(which curl) http://localhost:80/api/" +cli_silent_eval() { + log "$@" + eval "$@" >> "${LOGS}" 2>&1 } -get_docker_install_type() { -# debug $FUNCNAME - if is_boot2docker; then - echo "boot2docker" - elif is_docker_for_windows; then - echo "docker4windows" - elif is_docker_for_mac; then - echo "docker4mac" +is_log() { + if [ "${CHE_CLI_LOG}" = "true" ]; then + return 0 else - echo "native" + return 1 fi } -has_docker_for_windows_client(){ -# debug $FUNCNAME - if [[ "${GLOBAL_HOST_IP}" = "10.0.75.2" ]]; then +is_warning() { + if [ "${CHE_CLI_WARN}" = "true" ]; then return 0 else return 1 fi } -is_boot2docker() { -# debug $FUNCNAME - if uname -r | grep -q 'boot2docker'; then +is_info() { + if [ "${CHE_CLI_INFO}" = "true" ]; then return 0 else return 1 fi } -is_docker_for_windows() { -# debug $FUNCNAME - if uname -r | grep -q 'moby' && has_docker_for_windows_client; then +is_debug() { + if [ "${CHE_CLI_DEBUG}" = "true" ]; then return 0 else return 1 fi } -is_docker_for_mac() { -# debug $FUNCNAME - if uname -r | grep -q 'moby' && ! has_docker_for_windows_client; then +debug_server() { + if [ "${CHE_DEBUG}" = "true" ]; then return 0 else return 1 fi } -is_native() { -# debug $FUNCNAME - if [ $(get_docker_install_type) = "native" ]; then +is_fast() { + if [ "${FAST_BOOT}" = "true" ]; then return 0 else return 1 fi } -docker_run() { -# debug $FUNCNAME - # Setup options for connecting to docker host - if [ -z "${DOCKER_HOST+x}" ]; then - DOCKER_HOST="/var/run/docker.sock" +is_offline() { + if [ "${CHE_OFFLINE}" = "true" ]; then + return 0 + else + return 1 fi +} - echo "" > /tmp/docker_run_vars - # Add environment variables for CHE - while IFS='=' read -r -d '' key value; do - if [[ ${key} == "CHE_"* ]]; then - echo ${key}=${value} >> /tmp/docker_run_vars - fi - done < <(env -0) - - # Add scripts global variables for CHE - while read key; do - if [[ ${key} == "CHE_"* ]]; then - local ENV_VAL="${!key}" - echo ${key}=${ENV_VAL} >> /tmp/docker_run_vars - fi - done < <(compgen -v) - - - if [ -S "$DOCKER_HOST" ]; then - docker run --rm --env-file /tmp/docker_run_vars \ - -v $DOCKER_HOST:$DOCKER_HOST \ - -v $HOME:$HOME \ - -w "$(pwd)" "$@" +is_trace() { + if [ "${CHE_TRACE}" = "true" ]; then + return 0 else - docker run --rm --env-file /tmp/docker_run_vars \ - -e DOCKER_HOST -e DOCKER_TLS_VERIFY -e DOCKER_CERT_PATH \ - -v $HOME:$HOME \ - -w "$(pwd)" "$@" + return 1 fi } -container_exist_by_name(){ - docker inspect ${1} > /dev/null 2>&1 - if [ "$?" == "0" ]; then +skip_preflight() { + if [ "${CHE_SKIP_PREFLIGHT}" = "true" ]; then return 0 else return 1 fi } -get_server_container_id() { - log "docker inspect -f '{{.Id}}' ${1}" - docker inspect -f '{{.Id}}' ${1} 2>&1 || false +skip_postflight() { + if [ "${CHE_SKIP_POSTFLIGHT}" = "true" ]; then + return 0 + else + return 1 + fi } -container_is_running() { - if [ "$(docker ps -qa -f "status=running" -f "id=${1}" | wc -l)" -eq 0 ]; then - return 1 - else +skip_nightly() { + if [ "${CHE_SKIP_NIGHTLY}" = "true" ]; then return 0 + else + return 1 fi } -wait_until_container_is_running() { - CONTAINER_START_TIMEOUT=${1} +skip_network() { + if [ "${CHE_SKIP_NETWORK}" = "true" ]; then + return 0 + else + return 1 + fi +} - ELAPSED=0 - until container_is_running ${2} || [ ${ELAPSED} -eq "${CONTAINER_START_TIMEOUT}" ]; do - log "sleep 1" - sleep 1 - ELAPSED=$((ELAPSED+1)) - done +skip_pull() { + if [ "${CHE_SKIP_PULL}" = "true" ]; then + return 0 + else + return 1 + fi } local_repo() { @@ -202,6 +215,64 @@ local_assembly() { fi } +get_command_help() { + if [ "${CHE_COMMAND_HELP}" = "true" ]; then + return 0 + else + return 1 + fi +} + +init_logging() { + # Initialize CLI folder + CLI_DIR=$CHE_CONTAINER_ROOT + test -d "${CLI_DIR}" || mkdir -p "${CLI_DIR}" + + # Ensure logs folder exists + LOGS="${CLI_DIR}/cli.log" + LOG_INITIALIZED=true + + # Log date of CLI execution + log "$(date)" +} + +cli_init() { + CHE_HOST=$(eval "echo \$${CHE_PRODUCT_NAME}_HOST") + CHE_PORT=$(eval "echo \$${CHE_PRODUCT_NAME}_PORT") + + if [[ "$(eval "echo \$${CHE_PRODUCT_NAME}_HOST")" = "" ]]; then + info "Welcome to $CHE_FORMAL_PRODUCT_NAME!" + info "" + info "We did not auto-detect a valid HOST or IP address." + info "Pass ${CHE_PRODUCT_NAME}_HOST with your hostname or IP address." + info "" + info "Rerun the CLI:" + info " docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock" + info " -v :${CHE_CONTAINER_ROOT}" + info " -e ${CHE_PRODUCT_NAME}_HOST=" + info " $CHE_IMAGE_FULLNAME $*" + return 2; + fi + + if is_initialized; then + CHE_HOST_LOCAL=$(get_value_of_var_from_env_file ${CHE_PRODUCT_NAME}_HOST) + if [[ "${CHE_HOST}" != "${CHE_HOST_LOCAL}" ]]; then + warning "${CHE_PRODUCT_NAME}_HOST (${CHE_HOST}) overridden by ${CHE_ENVIRONMENT_FILE} (${CHE_HOST_LOCAL})" + fi + fi + + # Special function to perform special behaviors if you are running nightly version + verify_nightly_accuracy + + # Do not perform a version compatibility check if running upgrade command. + # The upgrade command has its own internal checks for version compatibility. + if [[ "$@" == *"upgrade"* ]]; then + verify_version_upgrade_compatibility + elif ! is_fast; then + verify_version_compatibility + fi +} + check_docker() { if ! has_docker; then error "Docker not found. Get it at https://docs.docker.com/engine/installation/." @@ -379,6 +450,19 @@ check_mounts() { CHE_HOST_DEVELOPMENT_REPO="${REPO_MOUNT}" CHE_CONTAINER_DEVELOPMENT_REPO="/repo" + # When we build eclipse/che-base, we insert the version of the repo it was built from into the image + if [[ -f "/repo/dockerfiles/base/scripts/base/startup_01_init.sh" ]]; then + CHE_REPO_BASE_VERSION=$(grep -hn CHE_BASE_API_VERSION= /repo/dockerfiles/base/scripts/base/startup_01_init.sh) + CHE_REPO_BASE_VERSION=${CHE_REPO_BASE_VERSION#*=} + else + CHE_REPO_BASE_VERSION=$CHE_BASE_API_VERSION + fi + + if [[ $CHE_BASE_API_VERSION != $CHE_REPO_BASE_VERSION ]]; then + warning "The CLI base image version ($CHE_BASE_API_VERSION) does not match your repo ($CHE_REPO_BASE_VERSION)" + warning "You have mounted :/repo and your repo branch does not match with the image." + fi + CHE_ASSEMBLY="${CHE_HOST_INSTANCE}/dev/${CHE_MINI_PRODUCT_NAME}-tomcat" if [[ ! -d "${CHE_CONTAINER_DEVELOPMENT_REPO}" ]] || [[ ! -d "${CHE_CONTAINER_DEVELOPMENT_REPO}/assembly" ]]; then @@ -438,14 +522,87 @@ check_mounts() { fi } -docker_compose() { -# debug $FUNCNAME +has_docker() { + hash docker 2>/dev/null && return 0 || return 1 +} - if has_compose; then - docker-compose "$@" - else - docker_run -v "${CHE_HOST_INSTANCE}":"${CHE_CONTAINER_INSTANCE}" \ - docker/compose:1.8.1 "$@" +get_container_folder() { + THIS_CONTAINER_ID=$(get_this_container_id) + FOLDER=$(get_container_host_bind_folder "$1" $THIS_CONTAINER_ID) + echo "${FOLDER:=not set}" +} + +get_this_container_id() { + hostname +} + +get_container_host_bind_folder() { + # BINDS in the format of var/run/docker.sock:/var/run/docker.sock :${CHE_CONTAINER_ROOT} + BINDS=$(docker inspect --format="{{.HostConfig.Binds}}" "${2}" | cut -d '[' -f 2 | cut -d ']' -f 1) + + # Remove /var/run/docker.sock:/var/run/docker.sock + #VALUE=${BINDS/\/var\/run\/docker\.sock\:\/var\/run\/docker\.sock/} + + # Remove leading and trailing spaces + VALUE2=$(echo "${BINDS}" | xargs) + + MOUNT="" + IFS=$' ' + for SINGLE_BIND in $VALUE2; do + case $SINGLE_BIND in + + # Fix for CHE-3863 - in case there is :Z after the mount for SELinux, add * + *$1*) + MOUNT="${MOUNT} ${SINGLE_BIND}" + echo "${MOUNT}" | cut -f1 -d":" | xargs + ;; + *) + # Super ugly - since we parse by space, if the next parameter is not a colon, then + # we know that next parameter is second part of a directory with a space in it. + if [[ ${SINGLE_BIND} != *":"* ]]; then + MOUNT="${MOUNT} ${SINGLE_BIND}" + else + MOUNT="" + fi + ;; + esac + done +} + +check_host_volume_mount() { + if is_boot2docker; then + warning "Boot2docker detected - ensure :/data is mounted to %userprofile%" + fi + + if file_system_writable "${CHE_CONTAINER_ROOT}/test"; then + delete_file_system_test "${CHE_CONTAINER_ROOT}/test" + else + error "Unable to write files to your host" + error "Have you enabled Docker to allow mounting host directories?" + error "Did you give our CLI rights to create files on your host?" + return 2; fi } +file_system_writable() { + echo 'test' > "${1}" + + if [[ -f "${1}" ]]; then + return 0 + else + return 1 + fi +} + +delete_file_system_test() { + rm -rf $1 > /dev/null 2>&1 +} + +is_boot2docker() { +# debug $FUNCNAME + if uname -r | grep -q 'boot2docker'; then + return 0 + else + return 1 + fi +} diff --git a/dockerfiles/base/scripts/base/startup_03_pre_networking.sh b/dockerfiles/base/scripts/base/startup_03_pre_networking.sh new file mode 100644 index 00000000000..ff07d87afc0 --- /dev/null +++ b/dockerfiles/base/scripts/base/startup_03_pre_networking.sh @@ -0,0 +1,244 @@ +#!/bin/sh +# Copyright (c) 2016 Codenvy, S.A. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +initiate_offline_or_network_mode(){ + # If you are using ${CHE_FORMAL_PRODUCT_NAME} in offline mode, images must be loaded here + # This is the point where we know that docker is working, but before we run any utilities + # that require docker. + if is_offline; then + info "init" "Importing ${CHE_MINI_PRODUCT_NAME} Docker images from tars..." + + if [ ! -d ${CHE_CONTAINER_OFFLINE_FOLDER} ]; then + warning "Skipping offline image loading - '${CHE_CONTAINER_OFFLINE_FOLDER}' not found" + else + IFS=$'\n' + for file in "${CHE_CONTAINER_OFFLINE_FOLDER}"/*.tar + do + if ! $(docker load < "${CHE_CONTAINER_OFFLINE_FOLDER}"/"${file##*/}" > /dev/null); then + error "Failed to restore ${CHE_MINI_PRODUCT_NAME} Docker images" + return 2; + fi + info "init" "Loading ${file##*/}..." + done + fi + else + # If we are here, then we want to run in networking mode. + # If we are in networking mode, we have had some issues where users have failed DNS networking. + # See: https://github.com/eclipse/che/issues/3266#issuecomment-265464165 + if ! is_fast && ! skip_network; then + # Removing this info line as it was appearing before initial CLI output +# info "cli" "Checking network... (hint: '--fast' skips nightly, version, network, and preflight checks)" + local HTTP_STATUS_CODE=$(curl -I -k dockerhub.com -s -o /dev/null --write-out '%{http_code}') + if [[ ! $HTTP_STATUS_CODE -eq "301" ]]; then + info "Welcome to $CHE_FORMAL_PRODUCT_NAME!" + info "" + info "We could not resolve DockerHub using DNS." + info "Either we cannot reach the Internet or Docker's DNS resolver needs a modification." + info "" + info "You can:" + info " 1. Modify Docker's DNS settings." + info " a. Docker for Windows & Mac have GUIs for this." + info " b. Typically setting DNS to 8.8.8.8 fixes resolver issues." + info " 2. Does your network require Docker to use a proxy?" + info " a. Docker for Windows & Mac have GUIs to set proxies." + info " 3. Verify that you have access to DockerHub." + info " a. Try 'curl --head dockerhub.com'" + info " 4. Skip networking checks." + info " a. Add '--fast' to any command" + return 2; + fi + fi + fi +} + +grab_initial_images() { + # get list of images + get_image_manifest ${CHE_VERSION} + + # grab all bootstrap images + IFS=$'\n' + for BOOTSTRAP_IMAGE_LINE in ${BOOTSTRAP_IMAGE_LIST}; do + local BOOTSTRAP_IMAGE=$(echo ${BOOTSTRAP_IMAGE_LINE} | cut -d'=' -f2) + if [ "$(docker images -q ${BOOTSTRAP_IMAGE} 2> /dev/null)" = "" ]; then + info "cli" "Pulling image ${BOOTSTRAP_IMAGE}" + log "docker pull ${BOOTSTRAP_IMAGE} >> \"${LOGS}\" 2>&1" + TEST="" + docker pull ${BOOTSTRAP_IMAGE} >> "${LOGS}" > /dev/null 2>&1 || TEST=$? + if [ "$TEST" = "1" ]; then + error "Image ${BOOTSTRAP_IMAGE} unavailable. Not on dockerhub or built locally." + return 2; + fi + fi + done + +} + +### Returns the list of ${CHE_FORMAL_PRODUCT_NAME} images for a particular version of ${CHE_FORMAL_PRODUCT_NAME} +### Sets the images as environment variables after loading from file +get_image_manifest() { + log "Checking registry for version '$1' images" + if ! has_version_registry $1; then + version_error $1 + return 1; + fi + + # Load images from file + BOOTSTRAP_IMAGE_LIST=$(cat ${SCRIPTS_BASE_CONTAINER_SOURCE_DIR}/images/images-bootstrap) + IMAGE_LIST=$(cat /version/$1/images) + UTILITY_IMAGE_LIST=$(cat ${SCRIPTS_BASE_CONTAINER_SOURCE_DIR}/images/images-utilities) + + # set variables + set_variables_images_list "${BOOTSTRAP_IMAGE_LIST}" + set_variables_images_list "${IMAGE_LIST}" + set_variables_images_list "${UTILITY_IMAGE_LIST}" +} + +has_version_registry() { + if [ -d /version/$1 ]; then + return 0; + else + return 1; + fi +} + +version_error(){ + text "\nWe could not find version '$1'. Available versions:\n" + list_versions + text "\nSet CHE_VERSION= and rerun.\n\n" +} + +### define variables for all image name in the given list +set_variables_images_list() { + IFS=$'\n' + for SINGLE_IMAGE in $1; do + log "eval $SINGLE_IMAGE" + eval $SINGLE_IMAGE + done + +} + +get_docker_install_type() { +# debug $FUNCNAME + if is_boot2docker; then + echo "boot2docker" + elif is_docker_for_windows; then + echo "docker4windows" + elif is_docker_for_mac; then + echo "docker4mac" + else + echo "native" + fi +} + +has_docker_for_windows_client(){ +# debug $FUNCNAME + if [[ "${GLOBAL_HOST_IP}" = "10.0.75.2" ]]; then + return 0 + else + return 1 + fi +} + +is_docker_for_windows() { +# debug $FUNCNAME + if uname -r | grep -q 'moby' && has_docker_for_windows_client; then + return 0 + else + return 1 + fi +} + +is_docker_for_mac() { +# debug $FUNCNAME + if uname -r | grep -q 'moby' && ! has_docker_for_windows_client; then + return 0 + else + return 1 + fi +} + +is_native() { +# debug $FUNCNAME + if [ $(get_docker_install_type) = "native" ]; then + return 0 + else + return 1 + fi +} + + +docker_run() { +# debug $FUNCNAME + # Setup options for connecting to docker host + if [ -z "${DOCKER_HOST+x}" ]; then + DOCKER_HOST="/var/run/docker.sock" + fi + + echo "" > /tmp/docker_run_vars + # Add environment variables for CHE + while IFS='=' read -r -d '' key value; do + if [[ ${key} == "CHE_"* ]]; then + echo ${key}=${value} >> /tmp/docker_run_vars + fi + done < <(env -0) + + # Add scripts global variables for CHE + while read key; do + if [[ ${key} == "CHE_"* ]]; then + local ENV_VAL="${!key}" + echo ${key}=${ENV_VAL} >> /tmp/docker_run_vars + fi + done < <(compgen -v) + + + if [ -S "$DOCKER_HOST" ]; then + docker run --rm --env-file /tmp/docker_run_vars \ + -v $DOCKER_HOST:$DOCKER_HOST \ + -v $HOME:$HOME \ + -w "$(pwd)" "$@" + else + docker run --rm --env-file /tmp/docker_run_vars \ + -e DOCKER_HOST -e DOCKER_TLS_VERIFY -e DOCKER_CERT_PATH \ + -v $HOME:$HOME \ + -w "$(pwd)" "$@" + fi +} + +has_curl() { + hash curl 2>/dev/null && return 0 || return 1 +} + +curl() { + + # In situations where we are performing internal pings using curl, then + # we should append the CHE_HOST as a no proxy. It seems that curl does + # not respect the NO_PROXY environment variable set on the system. + local NO_PROXY_CONFIG_FOR_CURL=("") + if [[ ! "${HTTP_PROXY}" = "" ]] || + [[ ! "${HTTPS_PROXY}" = "" ]]; then + if is_var_defined "${CHE_PRODUCT_NAME}_HOST"; then + NO_PROXY_CONFIG_FOR_CURL=("--noproxy" $(eval "echo \$${CHE_PRODUCT_NAME}_HOST")) + fi + fi + + if ! has_curl; then + log "docker run --rm --net=host appropriate/curl \"$@\"" + docker run --rm --net=host appropriate/curl ${NO_PROXY_CONFIG_FOR_CURL[@]} "$@" + else + log "$(which curl) ${NO_PROXY_CONFIG_FOR_CURL[@]} \"$@\"" + $(which curl) ${NO_PROXY_CONFIG_FOR_CURL[@]} "$@" + fi +} + +is_var_defined() { + if [ $# -ne 1 ] + then + echo "Expected exactly one argument: variable name as string, e.g., 'my_var'" + exit 1 + fi + eval "[ ! -z \${$1:-} ]" + return $? +} diff --git a/dockerfiles/base/scripts/base/startup_04_pre_cli_init.sh b/dockerfiles/base/scripts/base/startup_04_pre_cli_init.sh new file mode 100644 index 00000000000..60335c1a9da --- /dev/null +++ b/dockerfiles/base/scripts/base/startup_04_pre_cli_init.sh @@ -0,0 +1,309 @@ +#!/bin/sh +# Copyright (c) 2016 Codenvy, S.A. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html + +cli_pre_init() { + : +} + +cli_post_init() { + : +} + +cli_init() { + CHE_HOST=$(eval "echo \$${CHE_PRODUCT_NAME}_HOST") + CHE_PORT=$(eval "echo \$${CHE_PRODUCT_NAME}_PORT") + + if [[ "$(eval "echo \$${CHE_PRODUCT_NAME}_HOST")" = "" ]]; then + info "Welcome to $CHE_FORMAL_PRODUCT_NAME!" + info "" + info "We did not auto-detect a valid HOST or IP address." + info "Pass ${CHE_PRODUCT_NAME}_HOST with your hostname or IP address." + info "" + info "Rerun the CLI:" + info " docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock" + info " -v :${CHE_CONTAINER_ROOT}" + info " -e ${CHE_PRODUCT_NAME}_HOST=" + info " $CHE_IMAGE_FULLNAME $*" + return 2; + fi + + if is_initialized; then + CHE_HOST_LOCAL=$(get_value_of_var_from_env_file ${CHE_PRODUCT_NAME}_HOST) + if [[ "${CHE_HOST}" != "${CHE_HOST_LOCAL}" ]]; then + warning "${CHE_PRODUCT_NAME}_HOST (${CHE_HOST}) overridden by ${CHE_ENVIRONMENT_FILE} (${CHE_HOST_LOCAL})" + fi + fi + + # Special function to perform special behaviors if you are running nightly version + verify_nightly_accuracy + + # Do not perform a version compatibility check if running upgrade command. + # The upgrade command has its own internal checks for version compatibility. + if [[ "$@" == *"upgrade"* ]]; then + verify_version_upgrade_compatibility + elif ! is_fast; then + verify_version_compatibility + fi +} + +verify_nightly_accuracy() { + # Per request of the engineers, check to see if the locally cached nightly version is older + # than the one stored on DockerHub. + if is_nightly; then + + if ! is_fast && ! skip_nightly; then + local CURRENT_DIGEST=$(docker images -q --no-trunc --digests ${CHE_IMAGE_FULLNAME}) + + update_image $CHE_IMAGE_FULLNAME + + local NEW_DIGEST=$(docker images -q --no-trunc --digests ${CHE_IMAGE_FULLNAME}) + + if [[ "${CURRENT_DIGEST}" != "${NEW_DIGEST}" ]]; then + warning "Pulled new 'nightly' image - please rerun CLI" + return 2; + fi + fi + fi +} + +is_nightly() { + if [[ $(get_image_version) = "nightly" ]]; then + return 0 + else + return 1 + fi +} + +verify_version_compatibility() { + + ## If ! is_initialized, then the system hasn't been installed + ## First, compare the CLI image version to what version was initialized in /config/*.ver.donotmodify + ## - If they match, good + ## - If they don't match and one is nightly, fail + ## - If they don't match, then if CLI is older fail with message to get proper CLI + ## - If they don't match, then if CLLI is newer fail with message to run upgrade first + CHE_IMAGE_VERSION=$(get_image_version) + + # Only check for newer versions if not in offline mode and not nightly. + if ! is_offline && ! is_nightly; then + NEWER=$(compare_versions $CHE_IMAGE_VERSION) + + if [[ "${NEWER}" != "" ]]; then + warning "Newer version '$NEWER' available" + fi + fi + + if is_initialized; then + COMPARE_CLI_ENV=$(compare_cli_version_to_installed_version) + INSTALLED_VERSION=$(get_installed_version) + case "${COMPARE_CLI_ENV}" in + "match") + ;; + "nightly") + error "" + error "Your CLI version '${CHE_IMAGE_FULLNAME}' does not match your installed version '$INSTALLED_VERSION' in ${DATA_MOUNT}." + error "" + error "The 'nightly' CLI is only compatible with 'nightly' installed versions." + error "You may use 'nightly' with a separate ${CHE_FORMAL_PRODUCT_NAME} installation by providing a different ':/data' volume mount." + error "You may not '${CHE_MINI_PRODUCT_NAME} upgrade' from 'nightly' to a numbered (tagged) version." + error "" + error "Run the CLI as '${CHE_IMAGE_NAME}:' to install a tagged version." + return 2 + ;; + "install-less-cli") + error "" + error "Your CLI version '${CHE_IMAGE_FULLNAME}' is newer than your installed version '$INSTALLED_VERSION'." + error "" + error "Run '${CHE_IMAGE_FULLNAME} upgrade' to migrate your installation to '$CHE_IMAGE_VERSION'." + error "Or, run the CLI with '${CHE_IMAGE_NAME}:$INSTALLED_VERSION' to match the CLI with your installed version." + return 2 + ;; + "cli-less-install") + error "" + error "Your CLI version '${CHE_IMAGE_FULLNAME}' is older than your installed version '$INSTALLED_VERSION'." + error "" + error "You cannot use an older CLI with a newer installation." + error "" + error "Run the CLI with '${CHE_IMAGE_NAME}:$INSTALLED_VERSION' to match the CLI with your existing installed version." + return 2 + ;; + esac + fi +} + +verify_version_upgrade_compatibility() { + ## Two levels of checks + ## First, compare the CLI image version to what the admin has configured in /config/.env file + ## - If they match, nothing to upgrade + ## - If they don't match and one is nightly, fail upgrade is not supported for nightly + ## - If they don't match, then if CLI is older fail with message that we do not support downgrade + ## - If they don't match, then if CLI is newer then good + CHE_IMAGE_VERSION=$(get_image_version) + + if ! is_initialized || ! is_configured; then + info "upgrade" "$CHE_MINI_PRODUCT_NAME is not installed or configured. Nothing to upgrade." + return 2 + fi + + if is_initialized; then + COMPARE_CLI_ENV=$(compare_cli_version_to_installed_version) + CONFIGURED_VERSION=$(get_installed_version) + + case "${COMPARE_CLI_ENV}" in + "match") + error "" + error "Your CLI version '${CHE_IMAGE_FULLNAME}' is identical to your installed version '$CONFIGURED_VERSION'." + error "" + error "Run '${CHE_IMAGE_NAME}: upgrade' with a newer version to upgrade." + error "View available versions with '${CHE_IMAGE_FULLNAME} version'." + return 2 + ;; + "nightly") + error "" + error "Your CLI version '${CHE_IMAGE_FULLNAME}' or installed version '$CONFIGURED_VERSION' is nightly." + error "" + error "You may not '${CHE_IMAGE_NAME} upgrade' from 'nightly' to a numbered (tagged) version." + error "You can 'docker pull ${CHE_IMAGE_FULLNAME}' to get a newer nightly version." + return 2 + ;; + "install-less-cli") + ;; + "cli-less-install") + error "" + error "Your CLI version '${CHE_IMAGE_FULLNAME}' is older than your installed version '$CONFIGURED_VERSION'." + error "" + error "You cannot use '${CHE_IMAGE_NAME} upgrade' to downgrade versions." + error "" + error "Run '${CHE_IMAGE_NAME}: upgrade' with a newer version to upgrade." + error "View available versions with '${CHE_IMAGE_FULLNAME} version'." + return 2 + ;; + esac + fi +} + +get_installed_version() { + if ! is_initialized; then + echo "" + else + cat "${CHE_CONTAINER_INSTANCE}"/$CHE_VERSION_FILE + fi +} + +get_image_version() { + echo "$CHE_IMAGE_VERSION" +} + +less_than() { + for (( i=0; i<${#1}; i++ )); do + if [[ ${1:$i:1} != ${2:$i:1} ]]; then + if [ ${1:$i:1} -lt ${2:$i:1} ]; then + return 0 + else + return 1 + fi + fi + done + return 1 +} + +# Check if a version is < than another version (provide like for example : version_lt 5.0 5.1) +version_lt() { + test "$(printf '%s\n' "$@" | sort -V | head -n 1)" == "$1"; + return $?; +} + +# return true if the provided version is an intermediate version (like a Milestone or a Release Candidate) +is_intermediate_version() { + if [[ "$1" =~ .*-M.* ]]; then + return 0 + fi + if [[ "$1" =~ .*-RC.* ]]; then + return 0 + fi + return 1 +} + +compare_cli_version_to_installed_version() { + IMAGE_VERSION=$(get_image_version) + INSTALLED_VERSION=$(get_installed_version) + + # add -ZZ suffix if not intermediate version + # So for example 5.0.0 is transformed into 5.0.0-ZZ and is greater than 5.0.0-M8 + IMAGE_VERSION_SUFFIXED=${IMAGE_VERSION} + INSTALLED_VERSION_SUFFIXED=${INSTALLED_VERSION} + if ! is_intermediate_version ${IMAGE_VERSION}; then + IMAGE_VERSION_SUFFIXED="${IMAGE_VERSION}-ZZ" + fi + if ! is_intermediate_version ${INSTALLED_VERSION}; then + INSTALLED_VERSION_SUFFIXED="${INSTALLED_VERSION}-ZZ" + fi + + if [[ "$INSTALLED_VERSION" = "$IMAGE_VERSION" ]]; then + echo "match" + elif [ "$INSTALLED_VERSION" = "nightly" ] || + [ "$IMAGE_VERSION" = "nightly" ]; then + echo "nightly" + elif version_lt $INSTALLED_VERSION_SUFFIXED $IMAGE_VERSION_SUFFIXED; then + echo "install-less-cli" + else + echo "cli-less-install" + fi +} + +is_initialized() { + if [[ -d "${CHE_CONTAINER_INSTANCE}" ]] && \ + [[ -f "${CHE_CONTAINER_INSTANCE}"/$CHE_VERSION_FILE ]] && \ + [[ -f "${REFERENCE_CONTAINER_ENVIRONMENT_FILE}" ]]; then + return 0 + else + return 1 + fi +} + +is_configured() { + if [[ -d "${CHE_CONTAINER_INSTANCE}/config" ]] && \ + [[ -f "${CHE_CONTAINER_INSTANCE}/${CHE_VERSION_FILE}" ]]; then + return 0 + else + return 1 + fi +} + +update_image() { + if [ "${1}" == "--force" ]; then + shift + info "download" "Removing image $1" + log "docker rmi -f $1 >> \"${LOGS}\"" + docker rmi -f $1 >> "${LOGS}" 2>&1 || true + fi + + if [ "${1}" == "--pull" ]; then + shift + fi + + info "download" "Pulling image $1" + text "\n" + log "docker pull $1 >> \"${LOGS}\" 2>&1" + TEST="" + docker pull $1 || TEST=$? + if [ "$TEST" = "1" ]; then + error "Image $1 unavailable. Not on dockerhub or built locally." + return 2; + fi + text "\n" +} + +# Input is a variable that would exist in the che.env file such as CHE_HOST. +# We then lookup the vlaue of this variable and return it +get_value_of_var_from_env_file() { + local LOOKUP_LOCAL=$(docker_run --env-file="${REFERENCE_CONTAINER_ENVIRONMENT_FILE}" \ + ${BOOTSTRAP_IMAGE_ALPINE} sh -c "echo \$$1") + echo $LOOKUP_LOCAL + +} + diff --git a/dockerfiles/base/scripts/base/startup_05_pre_exec.sh b/dockerfiles/base/scripts/base/startup_05_pre_exec.sh new file mode 100644 index 00000000000..435dfc2021a --- /dev/null +++ b/dockerfiles/base/scripts/base/startup_05_pre_exec.sh @@ -0,0 +1,71 @@ +#!/bin/sh +# Copyright (c) 2016 Codenvy, S.A. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html + + +cli_load() { + # Library contains reusable functions + source "${SCRIPTS_BASE_CONTAINER_SOURCE_DIR}"/library.sh + + if [ -f "${SCRIPTS_CONTAINER_SOURCE_DIR}"/library.sh ]; then + source "${SCRIPTS_CONTAINER_SOURCE_DIR}"/library.sh + fi +} + +cli_parse () { + COMMAND="cmd_$1" + + case $1 in + init|config|start|stop|restart|backup|restore|info|offline|destroy|download|\ + rmi|upgrade|version|ssh|sync|action|test|compile|dir|help) + ;; + *) + error "You passed an unknown command." + usage + return 2 + ;; + esac +} + +cli_execute() { + cmd_lifecycle "$@" +} + +cmd_lifecycle() { + PRE_COMMAND="pre_cmd_$1" + POST_COMMAND="post_cmd_$1" + HELP_COMMAND="help_cmd_$1" + COMMAND="cmd_$1" + + if [ -f "${SCRIPTS_BASE_CONTAINER_SOURCE_DIR}"/commands/cmd_$1.sh ]; then + source "${SCRIPTS_BASE_CONTAINER_SOURCE_DIR}"/commands/cmd_$1.sh + fi + + if [ -f "${SCRIPTS_CONTAINER_SOURCE_DIR}"/cmd_$1.sh ]; then + source "${SCRIPTS_CONTAINER_SOURCE_DIR}"/cmd_$1.sh + fi + + shift + + if get_command_help; then + if [ -n "$(type -t $HELP_COMMAND)" ] && [ "$(type -t $HELP_COMMAND)" = function ]; then + eval $HELP_COMMAND "$@" + return 2 + else + error "No help function found for $1" + fi + fi + + if [ -n "$(type -t $PRE_COMMAND)" ] && [ "$(type -t $PRE_COMMAND)" = function ]; then + eval $PRE_COMMAND "$@" + fi + + eval $COMMAND "$@" + + if [ -n "$(type -t $POST_COMMAND)" ] && [ "$(type -t $POST_COMMAND)" = function ]; then + eval $POST_COMMAND "$@" + fi +} diff --git a/dockerfiles/cli/scripts/cmd_empty.sh b/dockerfiles/base/scripts/entrypoint.sh similarity index 86% rename from dockerfiles/cli/scripts/cmd_empty.sh rename to dockerfiles/base/scripts/entrypoint.sh index c2553cf7d0b..5b2631a2749 100644 --- a/dockerfiles/cli/scripts/cmd_empty.sh +++ b/dockerfiles/base/scripts/entrypoint.sh @@ -5,3 +5,7 @@ # which accompanies this distribution, and is available at # http://www.eclipse.org/legal/epl-v10.html # + +source /scripts/base/startup.sh +start "$@" + diff --git a/dockerfiles/build.include b/dockerfiles/build.include index b40a81056e7..ffef9844193 100755 --- a/dockerfiles/build.include +++ b/dockerfiles/build.include @@ -63,8 +63,6 @@ build() { printf "${RED}Failure when building docker image ${IMAGE_NAME}:${TAG}${NC}\n" exit 1 fi - - } check_docker() { @@ -83,10 +81,10 @@ docker_exec() { fi } -has_docker_for_windows_client(){ - GLOBAL_HOST_ARCH=$(docker version --format {{.Client}} | cut -d" " -f5) +has_docker_for_windows_client() { + GLOBAL_HOST_ARCH=$(docker version --format {{.Client}}) - if [ "${GLOBAL_HOST_ARCH}" = "windows" ]; then + if [[ "${GLOBAL_HOST_ARCH}" = *"windows"* ]]; then return 0 else return 1 diff --git a/dockerfiles/cli/Dockerfile b/dockerfiles/cli/Dockerfile index 6c84027ec7b..f62eaf3f7bd 100644 --- a/dockerfiles/cli/Dockerfile +++ b/dockerfiles/cli/Dockerfile @@ -14,5 +14,5 @@ FROM eclipse/che-base:nightly COPY scripts /scripts/ COPY version /version/ -RUN mkdir /che && chmod u+x /scripts/entrypoint.sh +RUN mkdir /che ENTRYPOINT ["/scripts/entrypoint.sh"] diff --git a/dockerfiles/cli/scripts/cli.sh b/dockerfiles/cli/scripts/cli.sh deleted file mode 100644 index fe14e6963a1..00000000000 --- a/dockerfiles/cli/scripts/cli.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 Codenvy, S.A. -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Eclipse Public License v1.0 -# which accompanies this distribution, and is available at -# http://www.eclipse.org/legal/epl-v10.html -# - -post_init() { - GLOBAL_HOST_IP=${GLOBAL_HOST_IP:=$(docker_run --net host ${BOOTSTRAP_IMAGE_CHEIP})} - DEFAULT_CHE_HOST=$GLOBAL_HOST_IP - CHE_HOST=${CHE_HOST:-${DEFAULT_CHE_HOST}} - DEFAULT_CHE_PORT=8080 - CHE_PORT=${CHE_PORT:-${DEFAULT_CHE_PORT}} - CHE_MIN_RAM=1.5 - CHE_MIN_DISK=100 - - DEFAULT_CHE_SERVER_CONTAINER_NAME="${CHE_MINI_PRODUCT_NAME}" - CHE_SERVER_CONTAINER_NAME="${CHE_SERVER_CONTAINER_NAME:-${DEFAULT_CHE_SERVER_CONTAINER_NAME}}" - - DEFAULT_CHE_CONTAINER_NAME="${CHE_SERVER_CONTAINER_NAME}" - CHE_CONTAINER_NAME="${CHE_CONTAINER:-${DEFAULT_CHE_CONTAINER_NAME}}" - - DEFAULT_CHE_CONTAINER_PREFIX="${CHE_SERVER_CONTAINER_NAME}" - CHE_CONTAINER_PREFIX="${CHE_CONTAINER_PREFIX:-${DEFAULT_CHE_CONTAINER_PREFIX}}" - - if [[ "${CHE_CONTAINER_NAME}" = "${CHE_MINI_PRODUCT_NAME}" ]]; then - if [[ "${CHE_PORT}" != "${DEFAULT_CHE_PORT}" ]]; then - CHE_CONTAINER_NAME="${CHE_CONTAINER_PREFIX}-${CHE_PORT}" - else - CHE_CONTAINER_NAME="${CHE_CONTAINER_PREFIX}" - fi - fi -} diff --git a/dockerfiles/cli/scripts/post_init.sh b/dockerfiles/cli/scripts/post_init.sh new file mode 100644 index 00000000000..30c7fd8e01a --- /dev/null +++ b/dockerfiles/cli/scripts/post_init.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# Copyright (c) 2016 Codenvy, S.A. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# + +post_init() { + GLOBAL_HOST_IP=${GLOBAL_HOST_IP:=$(docker_run --net host ${BOOTSTRAP_IMAGE_CHEIP})} + DEFAULT_CHE_HOST=$GLOBAL_HOST_IP + CHE_HOST=${CHE_HOST:-${DEFAULT_CHE_HOST}} +} diff --git a/dockerfiles/cli/scripts/entrypoint.sh b/dockerfiles/cli/scripts/pre_init.sh old mode 100755 new mode 100644 similarity index 77% rename from dockerfiles/cli/scripts/entrypoint.sh rename to dockerfiles/cli/scripts/pre_init.sh index 6a7243ad53f..ef3eb4cd136 --- a/dockerfiles/cli/scripts/entrypoint.sh +++ b/dockerfiles/cli/scripts/pre_init.sh @@ -15,8 +15,13 @@ pre_init() { ADDITIONAL_OPTIONAL_DOCKER_MOUNTS="" ADDITIONAL_COMMANDS="" ADDITIONAL_GLOBAL_OPTIONS="" -} + + # This must be incremented when BASE is incremented by an API developer + CHE_CLI_API_VERSION=1 -source /scripts/base/startup.sh -start "$@" + DEFAULT_CHE_PORT=8080 + CHE_PORT=${CHE_PORT:-${DEFAULT_CHE_PORT}} + CHE_MIN_RAM=1.5 + CHE_MIN_DISK=100 +} diff --git a/dockerfiles/cli/version/5.3.0/images b/dockerfiles/cli/version/5.3.0/images index 825519c54f0..7cd61c357f5 100644 --- a/dockerfiles/cli/version/5.3.0/images +++ b/dockerfiles/cli/version/5.3.0/images @@ -1,2 +1,3 @@ IMAGE_INIT=eclipse/che-init:5.3.0 IMAGE_CHE=eclipse/che-server:5.3.0 +IMAGE_COMPOSE=docker/compose:1.8.1 diff --git a/dockerfiles/cli/version/latest/images b/dockerfiles/cli/version/latest/images index 7f7a4a3f6c7..7952326908a 100644 --- a/dockerfiles/cli/version/latest/images +++ b/dockerfiles/cli/version/latest/images @@ -1,2 +1,3 @@ IMAGE_INIT=eclipse/che-init:latest IMAGE_CHE=eclipse/che-server:latest +IMAGE_COMPOSE=docker/compose:1.8.1 diff --git a/dockerfiles/cli/version/nightly/images b/dockerfiles/cli/version/nightly/images index 9b12bec4d0d..4c8d53c2005 100644 --- a/dockerfiles/cli/version/nightly/images +++ b/dockerfiles/cli/version/nightly/images @@ -1,2 +1,3 @@ IMAGE_INIT=eclipse/che-init:nightly IMAGE_CHE=eclipse/che-server:nightly +IMAGE_COMPOSE=docker/compose:1.8.1 diff --git a/dockerfiles/init/modules/che/manifests/init.pp b/dockerfiles/init/modules/che/manifests/init.pp index b8f27df46af..53431ff4551 100644 --- a/dockerfiles/init/modules/che/manifests/init.pp +++ b/dockerfiles/init/modules/che/manifests/init.pp @@ -6,4 +6,12 @@ content => template("che/che.env.erb"), mode => "644", } + + if $che_dev_env == "on" { + file { "/opt/che/che.sh": + ensure => "present", + content => template("che/che.sh.erb"), + mode => "644", + } + } } diff --git a/dockerfiles/init/modules/che/templates/che.sh.erb b/dockerfiles/init/modules/che/templates/che.sh.erb new file mode 100644 index 00000000000..45fb3c2f482 --- /dev/null +++ b/dockerfiles/init/modules/che/templates/che.sh.erb @@ -0,0 +1,251 @@ +#!/bin/bash +# +# Copyright (c) 2012-2017 Codenvy, S.A. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Codenvy, S.A. - initial API and implementation +# + +set -e +set +o posix + +determine_os () { + case "${OSTYPE}" in + linux*|freebsd*) + HOST="linux" + ;; + darwin*) + HOST="mac" + ;; + cygwin|msys|win32) + HOST="windows" + ;; + *) + # unknown option + error "We could not detect your operating system. Che is unlikely to work properly." + return 1 + ;; + esac +} + +init_global_variables () { + # For coloring console output + BLUE='\033[1;34m' + GREEN='\033[0;32m' + NC='\033[0m' + + USAGE=" +Usage: bash che.sh COMMAND + +Starts and stops Che's natively with Tomcat - this method is for developers and unsupported + +COMMANDS: + start Starts server with output in the background + stop Stops ${CHE_MINI_PRODUCT_NAME} server + run Starts server with output in the foreground + +Variables: + CHE_PORT The port the Che server will listen on + CHE_IP The IP address of the host - must be set if remote clients connecting + CHE_BLOCKING_ENTROPY Starts Tomcat with blocking entropy: -Djava.security.egd=file:/dev/./urandom + CHE_LOG_LEVEL [INFO | DEBUG] Sets the output level of Tomcat messages + CHE_DEBUG_SERVER If true, activates Tomcat's JPDA debugging mode + CHE_HOME Where the Che assembly resides - self-determining if not set +" + + # Use blocking entropy -- needed for some servers + DEFAULT_CHE_BLOCKING_ENTROPY=false + CHE_BLOCKING_ENTROPY=${CHE_BLOCKING_ENTROPY:-${DEFAULT_CHE_BLOCKING_ENTROPY}} + + DEFAULT_CHE_SERVER_ACTION=run + CHE_SERVER_ACTION=${CHE_SERVER_ACTION:-${DEFAULT_CHE_SERVER_ACTION}} + + # Must be exported as this will be needed by Tomcat's JVM + DEFAULT_CHE_REGISTRY_HOST=localhost + export CHE_REGISTRY_HOST=${CHE_REGISTRY_HOST:-${DEFAULT_CHE_REGISTRY_HOST}} + + DEFAULT_CHE_PORT=8080 + CHE_PORT=${CHE_PORT:-${DEFAULT_CHE_PORT}} + + DEFAULT_CHE_IP= + CHE_IP=${CHE_IP:-${DEFAULT_CHE_IP}} + + DEFAULT_CHE_LOG_LEVEL=INFO + CHE_LOG_LEVEL=${CHE_LOG_LEVEL:-${DEFAULT_CHE_LOG_LEVEL}} + + DEFAULT_CHE_DEBUG_SERVER=false + CHE_DEBUG_SERVER=${CHE_DEBUG_SERVER:-${DEFAULT_CHE_DEBUG_SERVER}} + + DEFAULT_CHE_HOME="${PWD}/dev/che-tomcat" + export CHE_HOME=${CHE_HOME:-${DEFAULT_CHE_HOME}} +} + +error() { + printf "${RED}ERROR:${NC} %s\n" "${1}" +} + +parse_command_line () { + if [ $# -gt 1 ]; then + usage + return 1 + fi + + case $1 in + start|stop|run) + CHE_SERVER_ACTION=$1 + ;; + -h|--help) + usage + return 1 + ;; + *) + # unknown option + usage + return 1 + ;; + esac +} + +usage () { + echo "${USAGE}" +} + +set_environment_variables () { + ### Set value of derived environment variables. + + # CHE_DOCKER_MACHINE_HOST is used internally by Che to set its IP address + if [[ -n "${CHE_IP}" ]]; then + export CHE_DOCKER_MACHINE_HOST="${CHE_IP}" + fi + + # Convert Tomcat environment variables to POSIX format. + if [[ "${JAVA_HOME}" == *":"* ]]; then + JAVA_HOME=$(echo /"${JAVA_HOME}" | sed 's|\\|/|g' | sed 's|:||g') + fi + + # Convert Che environment variables to POSIX format. + if [[ "${CHE_HOME}" == *":"* ]]; then + CHE_HOME=$(echo /"${CHE_HOME}" | sed 's|\\|/|g' | sed 's|:||g') + fi + + if [[ "${CHE_HOME}" =~ \ |\' ]] && [[ "${HOST}" == "windows" ]]; then + echo "!!!" + echo "!!! Ohhhhh boy." + echo "!!! You are on Windows and installed Che into a directory that contains a space." + echo "!!! Tomcat behaves badly because of this." + echo "!!!" + echo "!!! We attempted to work around this by converting your path to one without a space." + echo "!!! However, it seems that the drive where Che is installed does not allow this." + echo "!!! So we seem to be buggered." + echo "!!!" + echo "!!! You can fix this issue by installing Che into a directory without spaces in the name." + echo "!!! Isn't Windows fun? Long live William Shatner." + echo "!!!" + return 1 + fi + + # Che configuration directory - where che.properties lives + if [ -z "${CHE_LOCAL_CONF_DIR}" ]; then + export CHE_LOCAL_CONF_DIR="${CHE_HOME}/conf/" + fi + + # Sets the location of the application server and its executables + # Internal property - should generally not be overridden + export CATALINA_HOME="${CHE_HOME}/tomcat" + + # Convert windows path name to POSIX + if [[ "${CATALINA_HOME}" == *":"* ]]; then + CATALINA_HOME=$(echo /"${CATALINA_HOME}" | sed 's|\\|/|g' | sed 's|:||g') + fi + + # Internal properties - should generally not be overridden + export CATALINA_BASE="${CHE_HOME}/tomcat" + export ASSEMBLY_BIN_DIR="${CATALINA_HOME}/bin" + export CHE_LOGS_LEVEL="${CHE_LOG_LEVEL}" + export CHE_LOGS_DIR="${CATALINA_HOME}/logs/" +} + + +start_che_server () { + ######################################### + # Launch Che natively as a tomcat server + call_catalina +} + +stop_che_server () { + echo -e "Stopping Che server running on localhost:${CHE_PORT}" + call_catalina >/dev/null 2>&1 +} + +call_catalina () { + # Test to see that Che application server is where we expect it to be + if [ ! -d "${ASSEMBLY_BIN_DIR}" ]; then + error "Could not find Che's application server." + return 1; + fi + + if [ -z "${JAVA_HOME}" ]; then + error "JAVA_HOME is not set." + return 1; + fi + + # Test to see that Java is installed and working + "${JAVA_HOME}"/bin/java &>/dev/null || JAVA_EXIT=$? || true + if [ "${JAVA_EXIT}" != "1" ]; then + error "We could not find a working Java JVM. 'java' command fails." + return 1; + fi + + if [[ "${CHE_SKIP_JAVA_VERSION_CHECK}" == false ]]; then + # Che requires Java version 1.8 or higher. + JAVA_VERSION=$("${JAVA_HOME}"/bin/java -version 2>&1 | awk -F '"' '/version/ {print $2}') + if [ -z "${JAVA_VERSION}" ]; then + error "Failure running JAVA_HOME/bin/java -version. We received ${JAVA_VERSION}." + return 1; + fi + + if [[ "${JAVA_VERSION}" < "1.8" ]]; then + error "Che requires Java version 1.8 or higher. We found ${JAVA_VERSION}." + return 1; + fi + fi + + ### Initialize default JVM arguments to run che + if [[ "${CHE_BLOCKING_ENTROPY}" == true ]]; then + [ -z "${JAVA_OPTS}" ] && JAVA_OPTS="-Xms256m -Xmx1024m" + else + [ -z "${JAVA_OPTS}" ] && JAVA_OPTS="-Xms256m -Xmx1024m -Djava.security.egd=file:/dev/./urandom" + fi + + ### Cannot add this in setenv.sh. + ### We do the port mapping here, and this gets inserted into server.xml when tomcat boots + export JAVA_OPTS="${JAVA_OPTS} -Dport.http=${CHE_PORT} -Dche.home=${CHE_HOME}" + export SERVER_PORT=${CHE_PORT} + + # Launch the Che application server, passing in command line parameters + if [[ "${CHE_DEBUG_SERVER}" == true ]]; then + "${ASSEMBLY_BIN_DIR}"/catalina.sh jpda ${CHE_SERVER_ACTION} + else + "${ASSEMBLY_BIN_DIR}"/catalina.sh ${CHE_SERVER_ACTION} + fi +} + +execute_che () { + determine_os + init_global_variables + parse_command_line "$@" + set_environment_variables + + if [ "${CHE_SERVER_ACTION}" == "stop" ]; then + stop_che_server + else + start_che_server + fi +} + +# Run the finish function if exit signal initiated +execute_che "$@" \ No newline at end of file