#!/bin/bash # Copyright (c) 2018 Red Hat, Inc. # 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 # # This script is meant for quick & easy install of Che on OpenShift via: # # ``` bash # DEPLOY_SCRIPT_URL=https://raw.githubusercontent.com/eclipse/che/master/deploy/openshift/deploy_che.sh # curl -fsSL ${DEPLOY_SCRIPT_URL} -o get-che.sh # bash get-che.sh --wait-che # ``` # # For more deployment options: https://www.eclipse.org/che/docs/setup/openshift/index.html set -e # -------------------------------------------------------- # Check pre-requisites # -------------------------------------------------------- command -v oc >/dev/null 2>&1 || { echo >&2 "[CHE] [ERROR] Command line tool oc (https://docs.openshift.org/latest/cli_reference/get_started_cli.html) is required but it's not installed. Aborting."; exit 1; } command -v jq >/dev/null 2>&1 || { echo >&2 "[CHE] [ERROR] Command line tool jq (https://stedolan.github.io/jq) is required but it's not installed. Aborting."; exit 1; } # ---------------- # helper functions # ---------------- # inject_che_config injects che configuration in ENV format in deploy config # first arg is a marker string, second is a path to the file with parameters in KV format which will be inserted after marker inject_che_config() { while IFS= read -r line do printf '%s\n' "$line" if [[ "$line" == *"$1"* ]];then while read l; do #ignore comments and empty lines if [[ "$l" != "#"* ]] && [[ ! -z "$l" ]]; then # properly extract key and value from config map file KEY=$(echo $l | cut -d ' ' -f1 | cut -d ':' -f1) VALUE=$(eval echo $l | cut -d ':' -f2- | cut -d ' ' -f2-) # put key and value in proper format in to a yaml file after marker line printf '%s\n' " - name: $KEY" printf '%s\n' " value: \"$VALUE\"" fi done <$2 fi done < /dev/stdin } wait_until_che_is_available() { if [ -z "${CHE_API_ENDPOINT+x}" ]; then echo -n "[CHE] Inferring \$CHE_API_ENDPOINT..." che_host=$(oc get route che -o jsonpath='{.spec.host}') if [ -z "${che_host}" ]; then echo >&2 "[CHE] [ERROR] Failed to infer environment variable \$CHE_API_ENDPOINT. Aborting. Please set it and run ${0} script again."; exit 1; fi if [[ $(oc get route che -o jsonpath='{.spec.tls}') ]]; then protocol="https" ; else protocol="http"; fi CHE_API_ENDPOINT="${protocol}://${che_host}/api" echo "done (${CHE_API_ENDPOINT})" fi available=$(oc get dc che -o json | jq '.status.conditions[] | select(.type == "Available") | .status') progressing=$(oc get dc che -o json | jq '.status.conditions[] | select(.type == "Progressing") | .status') DEPLOYMENT_TIMEOUT_SEC=300 POLLING_INTERVAL_SEC=5 end=$((SECONDS+DEPLOYMENT_TIMEOUT_SEC)) while [ "${available}" != "\"True\"" ] && [ ${SECONDS} -lt ${end} ]; do available=$(oc get dc che -o json | jq '.status.conditions[] | select(.type == "Available") | .status') progressing=$(oc get dc che -o json | jq '.status.conditions[] | select(.type == "Progressing") | .status') timeout_in=$((end-SECONDS)) echo "[CHE] Deployment is in progress...(Available.status=${available}, Progressing.status=${progressing}, Timeout in ${timeout_in}s)" sleep ${POLLING_INTERVAL_SEC} done if [ "${progressing}" == "\"True\"" ]; then echo "[CHE] Che deployed successfully" elif [ "${progressing}" == "False" ]; then echo "[CHE] [ERROR] Che deployment failed. Aborting. Run command 'oc rollout status che' to get more details." exit 1 elif [ ${SECONDS} -lt ${end} ]; then echo "[CHE] [ERROR] Deployment timeout. Aborting." exit 1 fi che_http_status=$(curl -s -o /dev/null -I -w "%{http_code}" "${CHE_API_ENDPOINT}/system/state") if [ "${che_http_status}" == "200" ]; then echo "[CHE] Che is up and running" else echo "[CHE] [ERROR] Che is not responding (HTTP status= ${che_http_status})" exit 1 fi } # -------------- # Print Che logo # -------------- echo cat < docker.io\/rhchestage CHE_IMAGE_SANITIZED=$(echo "${CHE_IMAGE}" | sed 's/\//\\\//g') # Keycloak production endpoints are used by default CHE_KEYCLOAK_OSO_ENDPOINT=${CHE_KEYCLOAK_OSO_ENDPOINT:-${DEFAULT_CHE_KEYCLOAK_OSO_ENDPOINT}} KEYCLOAK_GITHUB_ENDPOINT=${KEYCLOAK_GITHUB_ENDPOINT:-${DEFAULT_KEYCLOAK_GITHUB_ENDPOINT}} get_che_pod_config() { DEFAULT_CHE_DEPLOYMENT_FILE_PATH=${BASE_DIR}/che-openshift.yml CHE_DEPLOYMENT_FILE_PATH=${CHE_DEPLOYMENT_FILE_PATH:-${DEFAULT_CHE_DEPLOYMENT_FILE_PATH}} DEFAULT_CHE_CONFIG_FILE_PATH=${BASE_DIR}/che-config CHE_CONFIG_FILE_PATH=${CHE_CONFIG_FILE_PATH:-${DEFAULT_CHE_CONFIG_FILE_PATH}} cat "${CHE_DEPLOYMENT_FILE_PATH}" | \ sed "s/ image:.*/ image: \"${CHE_IMAGE_SANITIZED}\"/" | \ sed "s/ imagePullPolicy:.*/ imagePullPolicy: \"${IMAGE_PULL_POLICY}\"/" | \ inject_che_config "#CHE_MASTER_CONFIG" "${CHE_CONFIG_FILE_PATH}" } # --------------------------------------- # Verify that we have all env var are set # --------------------------------------- if ([ -z "${OPENSHIFT_USERNAME+x}" ] || [ -z "${OPENSHIFT_PASSWORD+x}" ]) && [ -z "${OPENSHIFT_TOKEN+x}" ]; then echo "[CHE] **ERROR** Env var OPENSHIFT_USERNAME, OPENSHIFT_PASSWORD and OPENSHIFT_TOKEN are unset. You need to set username/password or token to continue. Aborting"; exit 1; fi if [ -z "${OPENSHIFT_ENDPOINT+x}" ]; then echo "[CHE] **ERROR**Env var OPENSHIFT_ENDPOINT is unset. You need to set it to continue. Aborting"; exit 1; fi if [ -z "${OPENSHIFT_NAMESPACE_URL+x}" ]; then echo "[CHE] **ERROR**Env var OPENSHIFT_NAMESPACE_URL is unset. You need to set it to continue. Aborting"; exit 1; fi # ----------------------------------- # Logging on to the OpenShift cluster # ----------------------------------- echo -n "[CHE] Logging on using OpenShift endpoint \"${OPENSHIFT_ENDPOINT}\"..." if [ -z "${OPENSHIFT_TOKEN+x}" ]; then oc login "${OPENSHIFT_ENDPOINT}" --insecure-skip-tls-verify="${OC_SKIP_TLS}" -u "${OPENSHIFT_USERNAME}" -p "${OPENSHIFT_PASSWORD}" > /dev/null OPENSHIFT_TOKEN=$(oc whoami -t) else oc login "${OPENSHIFT_ENDPOINT}" --insecure-skip-tls-verify="${OC_SKIP_TLS}" --token="${OPENSHIFT_TOKEN}" > /dev/null fi echo "done!" # ------------------------------------------------------------- # If command == cleanup then delete all openshift objects # ------------------------------------------------------------- if [ "${COMMAND}" == "cleanup" ]; then echo "[CHE] Deleting all OpenShift objects..." oc delete all --all echo "[CHE] Cleanup successfully started. Use \"oc get all\" to verify that all resources have been deleted." exit 0 # ------------------------------------------------------------- # If command == rollupdate then update Che # ------------------------------------------------------------- elif [ "${COMMAND}" == "rollupdate" ]; then echo "[CHE] Update CHE pod" get_che_pod_config | oc apply -f - echo "[CHE] Update successfully started" exit 0 # ---------------------------------------------------------------- # At this point command should be "deploy" otherwise it's an error # ---------------------------------------------------------------- elif [ "${COMMAND}" != "deploy" ]; then echo "[CHE] **ERROR**: Command \"${COMMAND}\" is not a valid command. Aborting." exit 1 fi # -------------------------- # Create project (if needed) # -------------------------- echo -n "[CHE] Checking if project \"${CHE_OPENSHIFT_PROJECT}\" exists..." if ! oc get project "${CHE_OPENSHIFT_PROJECT}" &> /dev/null; then if [ "${COMMAND}" == "cleanup" ] || [ "${COMMAND}" == "rollupdate" ]; then echo "**ERROR** project doesn't exist. Aborting"; exit 1; fi if [ "${OPENSHIFT_FLAVOR}" == "osio" ]; then echo "**ERROR** project doesn't exist on OSIO. Aborting"; exit 1; fi # OpenShift will not get project but project still exists for a period after being deleted. # The following will loop until it can create successfully. WAIT_FOR_PROJECT_TO_DELETE=true WAIT_FOR_PROJECT_TO_DELETE_MESSAGE="Waiting for project to be deleted fully(~15 seconds)..." echo "Project \"${CHE_OPENSHIFT_PROJECT}\" does not exist...trying to create it." DEPLOYMENT_TIMEOUT_SEC=120 POLLING_INTERVAL_SEC=2 timeout_in=$((POLLING_INTERVAL_SEC+DEPLOYMENT_TIMEOUT_SEC)) while $WAIT_FOR_PROJECT_TO_DELETE do { # try timeout_in=$((timeout_in-POLLING_INTERVAL_SEC)) if [ "$timeout_in" -le "0" ] ; then echo "[CHE] **ERROR**: Timeout of $DEPLOYMENT_TIMEOUT_SEC waiting for project \"${CHE_OPENSHIFT_PROJECT}\" to be deleted." exit 1 fi oc new-project "${CHE_OPENSHIFT_PROJECT}" &> /dev/null && \ WAIT_FOR_PROJECT_TO_DELETE=false # Only excutes if project creation is successfully } || { # catch echo -n $WAIT_FOR_PROJECT_TO_DELETE_MESSAGE WAIT_FOR_PROJECT_TO_DELETE_MESSAGE="." sleep $POLLING_INTERVAL_SEC } done echo "Project \"${CHE_OPENSHIFT_PROJECT}\" creation done!" else echo "Project \"${CHE_OPENSHIFT_PROJECT}\" already exists. Please remove project before running this script." exit 1 fi # ------------------------------------------------------------- # create CHE service and route # ------------------------------------------------------------- echo "[CHE] Creating serviceaccount, service and route for CHE pod" echo "apiVersion: v1 kind: List items: - apiVersion: v1 kind: ServiceAccount metadata: labels: app: che name: che - apiVersion: v1 kind: Service metadata: labels: app: che name: che-host spec: ports: - name: http port: 8080 protocol: TCP targetPort: 8080 selector: app: che - apiVersion: v1 kind: Route metadata: labels: app: che name: che spec: tls: insecureEdgeTerminationPolicy: Redirect termination: edge to: kind: Service name: che-host" | \ if [ "${ENABLE_SSL}" == "false" ]; then grep -v -e "tls:" -e "insecureEdgeTerminationPolicy: Redirect" -e "termination: edge" ; else cat -; fi | \ oc apply -f - # ------------------------------------------------------------- # Deploying secondary servers # for postgres and optionally Keycloak # ------------------------------------------------------------- if [[ "${CHE_MULTIUSER}" == "true" ]] && [[ "${COMMAND}" == "deploy" ]]; then if [ "${CHE_DEDICATED_KEYCLOAK}" == "true" ]; then "${BASE_DIR}"/multi-user/deploy_postgres_and_keycloak.sh else "${BASE_DIR}"/multi-user/deploy_postgres_only.sh fi fi # ------------------------------------------------------------- # Setting Keycloak-related environment variables # Done here since the Openshift project should be available # TODO Maybe this should go into a config map, but I don't know # How we would manage the retrieval of the Keycloak route # external URL. # ------------------------------------------------------------- if [ "${CHE_DEDICATED_KEYCLOAK}" == "true" ]; then CHE_KEYCLOAK_SERVER_ROUTE=$(oc get route keycloak -o jsonpath='{.spec.host}' || echo "") if [ "${CHE_KEYCLOAK_SERVER_ROUTE}" == "" ]; then echo "[CHE] **ERROR**: The dedicated Keycloak server should be deployed and visible through a route before starting the Che server" exit 1 fi CHE_POSTRES_SERVICE=$(oc get service postgres || echo "") if [ "${CHE_POSTRES_SERVICE}" == "" ]; then echo "[CHE] **ERROR**: The dedicated Postgres server should be started in Openshift project ${CHE_OPENSHIFT_PROJECT} before starting the Che server" exit 1 fi CHE_KEYCLOAK_AUTH__SERVER__URL=${CHE_KEYCLOAK_AUTH__SERVER__URL:-"${HTTP_PROTOCOL}://${CHE_KEYCLOAK_SERVER_ROUTE}/auth"} CHE_KEYCLOAK_REALM=${CHE_KEYCLOAK_REALM:-"che"} CHE_KEYCLOAK_CLIENT__ID=${CHE_KEYCLOAK_CLIENT__ID:-"che-public"} else CHE_KEYCLOAK_AUTH__SERVER__URL=${CHE_KEYCLOAK_AUTH__SERVER__URL:-"https://sso.openshift.io/auth"} CHE_KEYCLOAK_REALM=${CHE_KEYCLOAK_REALM:-"fabric8"} CHE_KEYCLOAK_CLIENT__ID=${CHE_KEYCLOAK_CLIENT__ID:-"openshiftio-public"} fi # ------------------------------------------------------------- # Verify that Che ServiceAccount has admin rights at project level # ------------------------------------------------------------- ## TODO we should create Che SA if it doesn't exist ## TODO we should check if che has admin rights before creating the role biding ## TODO if we are not in minishift we should fail if che SA doesn't have admin rights if [[ "${OPENSHIFT_FLAVOR}" =~ ^(minishift|ocp)$ ]]; then echo -n "[CHE] Setting admin role to \"che\" service account..." echo "apiVersion: v1 kind: RoleBinding metadata: name: che roleRef: name: admin subjects: - kind: ServiceAccount name: che" | oc apply -f - fi # ---------------------------------------------- # Get latest version of fabric8 tenant templates # ---------------------------------------------- # TODO make it possible to use a local Che template instead of always downloading it from maven central echo -n "[CHE] Retrieving latest version of fabric8 tenant Che template..." OSIO_VERSION=$(curl -sSL http://central.maven.org/maven2/io/fabric8/tenant/apps/che/maven-metadata.xml | grep latest | sed -e 's,.*\([^<]*\).*,\1,g') echo "done! (v.${OSIO_VERSION})" # -------------------------------------- # Applying resource quotas on minishift # -------------------------------------- if [ "${CHE_APPLY_RESOURCE_QUOTAS}" == "true" ] && [ "${OPENSHIFT_FLAVOR}" == "minishift" ]; then # Only cluster admin can set limitranges / resourcequotas oc login "${OPENSHIFT_ENDPOINT}" -u system:admin &> /dev/null echo "[CHE] Applying resource quotas for ${CHE_OPENSHIFT_PROJECT}" curl -sSL http://central.maven.org/maven2/io/fabric8/tenant/packages/fabric8-tenant-che-quotas-oso/"${OSIO_VERSION}"/fabric8-tenant-che-quotas-oso-"${OSIO_VERSION}"-openshift.yml | oc apply --force=true -f- echo "[CHE] Resource quotas have been successfully applied" oc login "${OPENSHIFT_ENDPOINT}" --token="${OPENSHIFT_TOKEN}" &> /dev/null fi # ---------------------------------------------- # Start the deployment # ---------------------------------------------- echo echo "[CHE] Deploying Che on ${OPENSHIFT_FLAVOR} (image ${CHE_IMAGE})" get_che_pod_config | oc apply --force=true -f - echo # -------------------------------- # Setup debugging routes if needed # -------------------------------- if [ "${CHE_DEBUG_SERVER}" == "true" ]; then if oc get svc che-debug &> /dev/null; then echo -n "[CHE] Deleting old che-debug service..." oc delete svc che-debug echo "done" fi echo -n "[CHE] Creating an OS route to debug Che wsmaster..." oc expose dc che --name=che-debug --target-port=http-debug --port=8000 --type=NodePort NodePort=$(oc get service che-debug -o jsonpath='{.spec.ports[0].nodePort}') echo "[CHE] Remote wsmaster debugging URL: ${MINISHIFT_IP}:${NodePort}" fi if [ "${WAIT_FOR_CHE}" == "true" ]; then wait_until_che_is_available fi if [ "${CHE_DEDICATED_KEYCLOAK}" == "true" ]; then "${BASE_DIR}"/multi-user/configure_keycloak.sh fi che_route=$(oc get route che -o jsonpath='{.spec.host}') echo echo "[CHE] Che deployment has been successufully bootstrapped" echo "[CHE] -> To check OpenShift deployment logs: 'oc get events -w'" echo "[CHE] -> To check Che server logs: 'oc logs -f dc/che'" echo "[CHE] -> Once the deployment is completed Che will be available at: " echo "[CHE] ${HTTP_PROTOCOL}://${che_route}" echo echo