# ========================================================================================= # Copyright (C) 2021 Orange & contributors # # This program is free software; you can redistribute it and/or modify it under the terms # of the GNU Lesser General Public License as published by the Free Software Foundation; # either version 3 of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License along with this # program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth # Floor, Boston, MA 02110-1301, USA. # ========================================================================================= variables: # Docker Image with OC CLI tool (can be overridden) OS_CLI_IMAGE: "openshift/origin-cli:latest" # [mandatory] OS_URL : the OpenShift API url # [mandatory] OS_TOKEN : the OpenShift authentication token # [optional] OS_REVIEW_PROJECT : the OpenShift project for review (defaults to $OS_DEFAULT_PROJECT) # [optional] OS_STAGING_PROJECT : the OpenShift project for staging (defaults to $OS_DEFAULT_PROJECT) # [optional] OS_PROD_PROJECT : the OpenShift project for production (defaults to $OS_DEFAULT_PROJECT) # [optional] OS_BASE_APP_NAME : the base application name (defaults to $CI_PROJECT_NAME) # [optional] OS_REVIEW_APP_NAME : specific OpenShift application name in review env (defaults to $OS_BASE_APP_NAME-$CI_COMMIT_REF_SLUG) # [optional] OS_STAGING_APP_NAME : specific OpenShift application name in staging env (defaults to $OS_BASE_APP_NAME-staging) # [optional] OS_PROD_APP_NAME : specific OpenShift application name in production env (defaults to $OS_BASE_APP_NAME) OS_BASE_APP_NAME: "$CI_PROJECT_NAME" OS_SCRIPTS_DIR: "." OS_BASE_TEMPLATE_NAME: "openshift" OS_REVIEW_ENVIRONMENT_SCHEME: "https" OS_CLEANUP_OBJECT_TYPES: "all,pvc,configmap,secret" # default production ref name (pattern) PROD_REF: '/^master$/' # default integration ref name (pattern) INTEG_REF: '/^develop$/' stages: - deploy - production .os-scripts: &os-scripts | # BEGSCRIPT set -e function log_info() { echo -e "[\\e[1;94mINFO\\e[0m] $*" } function log_warn() { echo -e "[\\e[1;93mWARN\\e[0m] $*" } function log_error() { echo -e "[\\e[1;91mERROR\\e[0m] $*" } function fail() { log_error "$*" exit 1 } function assert_defined() { if [[ -z "$1" ]] then log_error "$2" exit 1 fi } function install_ca_certs() { certs=$1 if [[ -z "$certs" ]] then return fi # import in system if echo "$certs" >> /etc/ssl/certs/ca-certificates.crt then log_info "CA certificates imported in \\e[33;1m/etc/ssl/certs/ca-certificates.crt\\e[0m" fi if echo "$certs" >> /etc/ssl/cert.pem then log_info "CA certificates imported in \\e[33;1m/etc/ssl/cert.pem\\e[0m" fi } function unscope_variables() { _scoped_vars=$(env | awk -F '=' "/^scoped__[a-zA-Z0-9_]+=/ {print \$1}" | sort) if [[ -z "$_scoped_vars" ]]; then return; fi log_info "Processing scoped variables..." for _scoped_var in $_scoped_vars do _fields=${_scoped_var//__/:} _condition=$(echo "$_fields" | cut -d: -f3) case "$_condition" in if) _not="";; ifnot) _not=1;; *) log_warn "... unrecognized condition \\e[1;91m$_condition\\e[0m in \\e[33;1m${_scoped_var}\\e[0m" continue ;; esac _target_var=$(echo "$_fields" | cut -d: -f2) _cond_var=$(echo "$_fields" | cut -d: -f4) _cond_val=$(eval echo "\$${_cond_var}") _test_op=$(echo "$_fields" | cut -d: -f5) case "$_test_op" in defined) if [[ -z "$_not" ]] && [[ -z "$_cond_val" ]]; then continue; elif [[ "$_not" ]] && [[ "$_cond_val" ]]; then continue; fi ;; equals|startswith|endswith|contains|in|equals_ic|startswith_ic|endswith_ic|contains_ic|in_ic) # comparison operator # sluggify actual value _cond_val=$(echo "$_cond_val" | tr '[:punct:]' '_') # retrieve comparison value _cmp_val_prefix="scoped__${_target_var}__${_condition}__${_cond_var}__${_test_op}__" _cmp_val=${_scoped_var#$_cmp_val_prefix} # manage 'ignore case' if [[ "$_test_op" == *_ic ]] then # lowercase everything _cond_val=$(echo "$_cond_val" | tr '[:upper:]' '[:lower:]') _cmp_val=$(echo "$_cmp_val" | tr '[:upper:]' '[:lower:]') fi case "$_test_op" in equals*) if [[ -z "$_not" ]] && [[ "$_cond_val" != "$_cmp_val" ]]; then continue; elif [[ "$_not" ]] && [[ "$_cond_val" == "$_cmp_val" ]]; then continue; fi ;; startswith*) if [[ -z "$_not" ]] && [[ "$_cond_val" != "$_cmp_val"* ]]; then continue; elif [[ "$_not" ]] && [[ "$_cond_val" == "$_cmp_val"* ]]; then continue; fi ;; endswith*) if [[ -z "$_not" ]] && [[ "$_cond_val" != *"$_cmp_val" ]]; then continue; elif [[ "$_not" ]] && [[ "$_cond_val" == *"$_cmp_val" ]]; then continue; fi ;; contains*) if [[ -z "$_not" ]] && [[ "$_cond_val" != *"$_cmp_val"* ]]; then continue; elif [[ "$_not" ]] && [[ "$_cond_val" == *"$_cmp_val"* ]]; then continue; fi ;; in*) if [[ -z "$_not" ]] && [[ "__${_cmp_val}__" != *"__${_cond_val}__"* ]]; then continue; elif [[ "$_not" ]] && [[ "__${_cmp_val}__" == *"__${_cond_val}__"* ]]; then continue; fi ;; esac ;; *) log_warn "... unrecognized test operator \\e[1;91m${_test_op}\\e[0m in \\e[33;1m${_scoped_var}\\e[0m" continue ;; esac # matches _val=$(eval echo "\$${_target_var}") log_info "... apply \\e[32m${_target_var}\\e[0m from \\e[32m\$${_scoped_var}\\e[0m${_val:+ (\\e[33;1moverwrite\\e[0m)}" _val=$(eval echo "\$${_scoped_var}") export "${_target_var}"="${_val}" done log_info "... done" } # evaluate and export a secret # - $1: secret variable name function eval_secret() { name=$1 value=$(eval echo "\$${name}") case "$value" in @b64@*) decoded=$(mktemp) errors=$(mktemp) if echo "$value" | cut -c6- | base64 -d > "${decoded}" 2> "${errors}" then # shellcheck disable=SC2086 export ${name}="$(cat ${decoded})" log_info "Successfully decoded base64 secret \\e[33;1m${name}\\e[0m" else fail "Failed decoding base64 secret \\e[33;1m${name}\\e[0m:\\n$(sed 's/^/... /g' "${errors}")" fi ;; @hex@*) decoded=$(mktemp) errors=$(mktemp) if echo "$value" | cut -c6- | sed 's/\([0-9A-F]\{2\}\)/\\\\x\1/gI' | xargs printf > "${decoded}" 2> "${errors}" then # shellcheck disable=SC2086 export ${name}="$(cat ${decoded})" log_info "Successfully decoded hexadecimal secret \\e[33;1m${name}\\e[0m" else fail "Failed decoding hexadecimal secret \\e[33;1m${name}\\e[0m:\\n$(sed 's/^/... /g' "${errors}")" fi ;; @url@*) url=$(echo "$value" | cut -c6-) if command -v curl > /dev/null then decoded=$(mktemp) errors=$(mktemp) if curl -s -S -f --connect-timeout 5 -o "${decoded}" "$url" 2> "${errors}" then # shellcheck disable=SC2086 export ${name}="$(cat ${decoded})" log_info "Successfully curl'd secret \\e[33;1m${name}\\e[0m" else fail "Failed getting secret \\e[33;1m${name}\\e[0m:\\n$(sed 's/^/... /g' "${errors}")" fi elif command -v wget > /dev/null then decoded=$(mktemp) errors=$(mktemp) if wget -T 5 -O "${decoded}" "$url" 2> "${errors}" then # shellcheck disable=SC2086 export ${name}="$(cat ${decoded})" log_info "Successfully wget'd secret \\e[33;1m${name}\\e[0m" else fail "Failed getting secret \\e[33;1m${name}\\e[0m:\\n$(sed 's/^/... /g' "${errors}")" fi else fail "Couldn't get secret \\e[33;1m${name}\\e[0m: no http client found" fi ;; esac } function eval_all_secrets() { encoded_vars=$(env | grep -v '^scoped__' | awk -F '=' '/^[a-zA-Z0-9_]*=@(b64|hex|url)@/ {print $1}') for var in $encoded_vars do eval_secret "$var" done } # Converts a string to SCREAMING_SNAKE_CASE function to_ssc() { echo "$1" | tr '[:lower:]' '[:upper:]' | tr '[:punct:]' '_' } function pre_apply() { # maybe execute pre apply script prescript="$OS_SCRIPTS_DIR/os-pre-apply.sh" if [[ -f "$prescript" ]] then log_info "--- \\e[32mpre-apply hook\\e[0m (\\e[33;1m${prescript}\\e[0m) found: execute" bash "$prescript" else log_info "--- \\e[32mpre-apply hook\\e[0m (\\e[33;1m${prescript}\\e[0m) not found: skip" fi } # extracts parameters from template and builds the param arguments string for each param found in: # 1) specific dotenv file (param 2) # 2) global dotenv file (param 1) # 3) env # The returned string is of the form: --param=param1="$param1" --param=param2="$param2" ... # /!\ this string has to be eval'd but allows using multiline parameters (a TLS certificate for instance) function build_template_param_args() { global_env="$1" spec_env="$2" oc process --parameters -f - | awk 'NR > 1{print $1}' | while read -r param_name do # 1: look for param into specific dotenv file if grep -e "^$param_name=" "$spec_env" >/dev/null 2>&1 then # force double-quote value val=$(grep -e "^$param_name=" "$spec_env" | cut -d'=' -f2- | sed -e 's/^ *//g' -e 's/ *$//g') if [[ "$val" != \"*\" ]]; then val="\"$val\""; fi echo -n "--param ${param_name}=${val} " # 2: look for param into global dotenv file elif grep -e "^$param_name=" "$global_env" >/dev/null 2>&1 then # force double-quote value val=$(grep -e "^$param_name=" "$global_env" | cut -d'=' -f2- | sed -e 's/^ *//g' -e 's/ *$//g') if [[ "$val" != \"*\" ]]; then val="\"$val\""; fi echo -n "--param ${param_name}=${val} " # 3: look for param into environment elif [[ -n "${!param_name}" ]] then echo -n "--param ${param_name}=\"\$${param_name}\" " fi done } # $1: templatefile function apply() { export templatefile=$1 # oc apply with params substitution log_info "--- \\e[32moc apply\\e[0m" log_info "--- template: \\e[33;1m${templatefile}\\e[0m" # evaluate template parameters from environment and dotenv files param_args=$(build_template_param_args "${OS_SCRIPTS_DIR}/${OS_BASE_TEMPLATE_NAME}.env" "${OS_SCRIPTS_DIR}/${OS_BASE_TEMPLATE_NAME}-${env}.env" < "$templatefile") # set label 'app' and 'env' on all created objects ($OS_STAGE_LABEL for backwards compatibility) echo "oc process --labels \"${OS_ENV_LABEL:-${OS_STAGE_LABEL:-env}}=$env,${OS_APP_LABEL:-app}=$appname\" $param_args -f $templatefile" eval oc process --labels "${OS_ENV_LABEL:-${OS_STAGE_LABEL:-env}}=$env,${OS_APP_LABEL:-app}=$appname" "$param_args" -f "$templatefile" | oc ${TRACE+--loglevel=6} apply -f - } function check_readiness() { # maybe execute readiness check script readycheck="$OS_SCRIPTS_DIR/os-readiness-check.sh" if [[ -f "$readycheck" ]] then log_info "--- \\e[32mreadiness-check hook\\e[0m (\\e[33;1m${readycheck}\\e[0m) found: execute" bash "$readycheck" else log_info "--- \\e[32mreadiness-check hook\\e[0m (\\e[33;1m${readycheck}\\e[0m) not found: assume app is ready" fi } function post_apply() { # maybe execute post apply script postscript="$OS_SCRIPTS_DIR/os-post-apply.sh" if [[ -f "$postscript" ]] then log_info "--- \\e[32mpost-apply hook\\e[0m (\\e[33;1m${postscript}\\e[0m) found: execute" bash "$postscript" else log_info "--- \\e[32mpost-apply hook\\e[0m (\\e[33;1m${postscript}\\e[0m) not found: skip" fi } function deploy() { # export project as it may be usefull to build image name based on internal registry (ex: docker-registry.default.svc:5000/${project}/${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_LABEL} ) project=$(oc project -q) export project export env=$1 # for backward compatibility export stage=$1 export appname=$2 # also export appname in SCREAMING_SNAKE_CASE format (may be useful with OpenShift env variables) appname_ssc=$(to_ssc "$2") export appname_ssc # extract hostname from $CI_ENVIRONMENT_URL hostname=$(echo "$CI_ENVIRONMENT_URL" | awk -F[/:] '{print $4}') export hostname log_info "--- \\e[32mdeploy\\e[0m (env: \\e[33;1m${env}\\e[0m)" log_info "--- looking for OS scripts in directory: \\e[33;1m${OS_SCRIPTS_DIR}\\e[0m" log_info "--- project: \\e[33;1m${project}\\e[0m" log_info "--- appname: \\e[33;1m${appname}\\e[0m" log_info "--- appname_ssc: \\e[33;1m${appname_ssc}\\e[0m" log_info "--- env: \\e[33;1m${env}\\e[0m" log_info "--- hostname: \\e[33;1m${hostname}\\e[0m" # maybe execute deploy script deployscript=$(ls -1 "$OS_SCRIPTS_DIR/os-deploy-${env}.sh" 2>/dev/null || ls -1 "$OS_SCRIPTS_DIR/os-deploy.sh" 2>/dev/null || echo "") if [[ -f "$deployscript" ]] then log_info "--- deploy script (\\e[33;1m${deployscript}\\e[0m) found: execute" bash "$deployscript" else log_info "--- no deploy script found: run template-based deployment" # find template templatefile=$(ls -1 "$OS_SCRIPTS_DIR/${OS_BASE_TEMPLATE_NAME}-${env}.yml" 2>/dev/null || ls -1 "$OS_SCRIPTS_DIR/${OS_BASE_TEMPLATE_NAME}.yml" 2>/dev/null || echo "") if [[ -z "$templatefile" ]] then log_error "--- no template" exit 1 fi pre_apply apply "$templatefile" post_apply fi check_readiness # finally persist environment url echo "$CI_ENVIRONMENT_URL" > environment_url.txt echo -e "environment_type=$env\\nenvironment_name=$appname\\nenvironment_url=$CI_ENVIRONMENT_URL" > openshift.env } # $1 deployment name function last_rollout_status() { oc rollout history "dc/$1" | sed -e '1,2d' -e '$d' | tail -1 | awk -F" " '{print tolower($2)}' } # $1 deployment name function force_rollout() { last_status=$(last_rollout_status "$1") if [ "$last_status" != "running" ] && [ "$last_status" != "pending" ] then log_info "Force rollout as last status is $last_status" oc rollout latest "$1" else log_info "A rollout is currently processing with status $last_status" fi } # $1 deployment name # $2 timeout (default is 2 minutes) function poll_last_rollout() { deployment_name=$1 timeout=${2:-120} sleep_time=10 waiting_time=0 still_running=1 status="running" log_info "Waiting for $deployment_name to finish its rollout. It will poll the last rollout status every $sleep_time seconds until $timeout seconds" while [ $waiting_time -lt "$timeout" ] && [ $still_running -eq 1 ] do log_info "$((timeout - waiting_time)) seconds remaining" status=$(last_rollout_status "$deployment_name") if [ "$status" == "running" ] || [ "$status" == "pending" ] then log_info "Last status is $status. Sleep $sleep_time...." sleep $sleep_time waiting_time=$((waiting_time + sleep_time)) else log_info "Rollout ended with status $status" still_running=0 fi done case "$status" in complete) log_info "Rollout complete" ;; failed) log_error "Rollout failed" exit 1 ;; running|pending) log_error "Rollout still $status after $waiting_time seconds. Increase timeout" exit 1 ;; *) log_error "Unknown rollout status: $status" exit 1 ;; esac } function delete() { export env=$1 # for backward compatibility export stage=$1 export appname=$2 # also export appname in SCREAMING_SNAKE_CASE format (may be useful with OpenShift env variables) appname_ssc=$(to_ssc "$2") export appname_ssc log_info "--- \\e[32mdelete\\e[0m (env: ${env})" log_info "--- appname: \\e[33;1m${appname}\\e[0m" log_info "--- appname_ssc: \\e[33;1m${appname_ssc}\\e[0m" log_info "--- env: \\e[33;1m${env}\\e[0m" # maybe execute cleanup script cleanupscript=$(ls -1 "$OS_SCRIPTS_DIR/os-cleanup-${env}.sh" 2>/dev/null || ls -1 "$OS_SCRIPTS_DIR/os-cleanup.sh" 2>/dev/null || echo "") if [[ -f "$cleanupscript" ]] then log_info "--- cleanup script (\\e[33;1m${cleanupscript}\\e[0m) found: execute" bash "$cleanupscript" else log_info "--- no cleanup script found: proceed with template-based delete" # maybe execute pre cleanup script prescript="$OS_SCRIPTS_DIR/os-pre-cleanup.sh" if [[ -f "$prescript" ]] then log_info "--- \\e[32mpre-cleanup hook\\e[0m (\\e[33;1m${prescript}\\e[0m) found: execute" bash "$prescript" else log_info "--- \\e[32mpre-cleanup hook\\e[0m (\\e[33;1m${prescript}\\e[0m) not found: skip" fi # delete app log_info "--- \\e[32moc delete\\e[0m" # delete all objects with label 'app=$appname' oc ${TRACE+--loglevel=6} delete "${OS_CLEANUP_OBJECT_TYPES}" --selector "${OS_APP_LABEL:-app}=$appname" # maybe execute post cleanup script postscript="$OS_SCRIPTS_DIR/os-post-cleanup.sh" if [[ -f "$postscript" ]] then log_info "--- \\e[32mpost-cleanup hook\\e[0m (\\e[33;1m${postscript}\\e[0m) found: execute" bash "$postscript" else log_info "--- \\e[32mpost-cleanup hook\\e[0m (\\e[33;1m${postscript}\\e[0m) not found: skip" fi fi } function delete_all() { export env=$1 appnameproto=$2 # make appname regex by replacing $CI_COMMIT_REF_SLUG with .* appnameregex=$(echo "$appnameproto" | sed -r "s/$CI_COMMIT_REF_SLUG/.*/g") # list services | remove 1st line | pick 7th column (selector) | filter services with label "app=$regex" matchingselectors=$(oc get svc -o wide | tail -n +2 | awk '{print $7}' | awk "/${OS_APP_LABEL:-app}=$appnameregex/ {print \$1}") matchcount=$(echo "$matchingselectors" | wc -w) log_info "--- \\e[32mdelete all\\e[0m (env: \\e[33;1m${env}\\e[0m, appname matcher: \\e[33;1m${appnameregex}\\e[0m): \\e[33;1m${matchcount}\\e[0m apps found" rc=0 for appselector in $matchingselectors do # extract review appname from selector (can be 'app=name,env=env,foo=bar') matchingapp=$(echo "$appselector" | sed -E "s/${OS_APP_LABEL:-app}=([^,]*).*/\\1/") echo -e "\\e[1;93m-------------------------------------------------------------------------------\\e[0m" if ! delete "$env" "$matchingapp" then log_warn "... failed deleting review app \\e[33;1m${matchingapp}\\e[0m (see logs)" rc=1 fi done return $rc } function extract_oldest_tags_from_image_stream_definition() { # $1 the number of oldest image to extract # $2: optional: file to extract tags . If not specified will take input from stdin # shellcheck disable=SC2016 jq -c -r --argjson limit "$1" '[ stderr | .status.tags[] | { tag: .tag, created: .items | max_by(.created ) | .created } ] | sort_by(.created) | reverse | .[$limit:] | .[].tag' < "${2:-/dev/stdin}" } function purge_old_image_tags() { # $1 image name # $2 number of image tags to keep log_info "Keeping last $2 images" oc get -o json "is/$1" | extract_oldest_tags_from_image_stream_definition "$2" | while read -r tag do log_info "Removing image stream tag $1:$tag" oc delete "istag/$1:$tag" done } function get_latest_template_version() { tag_json=$(wget -T 5 -q -O - "$CI_API_V4_URL/projects/to-be-continuous%2F$1/repository/tags?per_page=1" || echo "") echo "$tag_json" | sed -rn 's/^.*"name":"([^"]*)".*$/\1/p' } function check_for_update() { template="$1" actual="$2" latest=$(get_latest_template_version "$template") if [[ -n "$latest" ]] && [[ "$latest" != "$actual" ]] then log_warn "\\e[1;93m=======================================================================================================\\e[0m" log_warn "\\e[93mThe template \\e[32m$template\\e[93m:\\e[33m$actual\\e[93m you're using is not up-to-date: consider upgrading to version \\e[32m$latest\\e[0m" log_warn "\\e[93m(set \$TEMPLATE_CHECK_UPDATE_DISABLED to disable this message)\\e[0m" log_warn "\\e[1;93m=======================================================================================================\\e[0m" fi } if [[ -z "$TEMPLATE_CHECK_UPDATE_DISABLED" ]]; then check_for_update openshift "1.2.0"; fi # export tool functions (might be used in after_script) export -f log_info log_warn log_error assert_defined last_rollout_status force_rollout poll_last_rollout extract_oldest_tags_from_image_stream_definition purge_old_image_tags unscope_variables eval_all_secrets # ENDSCRIPT # Generic OS job .os-base: image: $OS_CLI_IMAGE services: - name: "$CI_REGISTRY/to-be-continuous/tools/tracking:master" command: ["--service", "openshift", "1.2.0" ] before_script: - *os-scripts - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}" # Deploy job prototype # Can be extended to define a concrete environment # # @arg ENV_TYPE : environment type # @arg ENV_APP_NAME : env-specific application name # @arg ENV_APP_SUFFIX: env-specific application suffix # @arg ENV_URL : env-specific OpenShift API url # @arg ENV_TOKEN : env-specific OpenShift API token # @arg ENV_PROJECT : env-specific OpenShift project name .os-deploy: extends: .os-base stage: deploy variables: ENV_APP_SUFFIX: "-$CI_ENVIRONMENT_SLUG" before_script: - *os-scripts - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}" - assert_defined "${ENV_URL:-$OS_URL}" 'Missing required OpenShift url' - assert_defined "${ENV_TOKEN:-$OS_TOKEN}" 'Missing required OpenShift token' - assert_defined "$ENV_PROJECT" 'Missing required OpenShift project' - oc login ${ENV_URL:-$OS_URL} --token=${ENV_TOKEN:-$OS_TOKEN} -n $ENV_PROJECT script: - deploy "$ENV_TYPE" "${ENV_APP_NAME:-${OS_BASE_APP_NAME}${ENV_APP_SUFFIX}}" artifacts: name: "$ENV_TYPE env url for $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG" paths: - environment_url.txt reports: dotenv: openshift.env # Cleanup job prototype # Can be extended for each deletable environment # # @arg ENV_TYPE : environment type # @arg ENV_APP_NAME : env-specific application name # @arg ENV_APP_SUFFIX: env-specific application suffix # @arg ENV_URL : env-specific OpenShift API url # @arg ENV_TOKEN : env-specific OpenShift API token # @arg ENV_PROJECT : env-specific OpenShift project name .os-cleanup: extends: .os-base stage: deploy # force no dependencies dependencies: [] variables: ENV_APP_SUFFIX: "-$CI_ENVIRONMENT_SLUG" before_script: - *os-scripts - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}" - assert_defined "${ENV_URL:-$OS_URL}" 'Missing required OpenShift url' - assert_defined "${ENV_TOKEN:-$OS_TOKEN}" 'Missing required OpenShift token' - assert_defined "$ENV_PROJECT" 'Missing required OpenShift project' - oc login ${ENV_URL:-$OS_URL} --token=${ENV_TOKEN:-$OS_TOKEN} -n $ENV_PROJECT script: - delete "$ENV_TYPE" "${ENV_APP_NAME:-${OS_BASE_APP_NAME}${ENV_APP_SUFFIX}}" environment: action: stop # deploy to review env (only for branches) # disabled by default, enable this job by setting # $OS_REVIEW_PROJECT. os-review: extends: .os-deploy variables: ENV_TYPE: review ENV_APP_NAME: "$OS_REVIEW_APP_NAME" ENV_URL: "$OS_REVIEW_URL" ENV_TOKEN: "$OS_REVIEW_TOKEN" ENV_PROJECT: "$OS_REVIEW_PROJECT" environment: name: review/$CI_COMMIT_REF_NAME url: "${OS_REVIEW_ENVIRONMENT_SCHEME}://${CI_PROJECT_NAME}-${CI_ENVIRONMENT_SLUG}.${OS_REVIEW_ENVIRONMENT_DOMAIN}" on_stop: os-cleanup-review resource_group: review/$CI_COMMIT_REF_NAME rules: # exclude merge requests - if: $CI_MERGE_REQUEST_ID when: never # exclude tags - if: $CI_COMMIT_TAG when: never # exclude if $CLEANUP_ALL_REVIEW set to 'force' - if: '$CLEANUP_ALL_REVIEW == "force"' when: never # only on non-production, non-integration branches, with $OS_REVIEW_PROJECT set - if: '$OS_REVIEW_PROJECT && $CI_COMMIT_REF_NAME !~ $PROD_REF && $CI_COMMIT_REF_NAME !~ $INTEG_REF' # stop review env (automatically triggered once branches are deleted) os-cleanup-review: extends: .os-cleanup variables: ENV_TYPE: review ENV_APP_NAME: "$OS_REVIEW_APP_NAME" ENV_URL: "$OS_REVIEW_URL" ENV_TOKEN: "$OS_REVIEW_TOKEN" ENV_PROJECT: "$OS_REVIEW_PROJECT" environment: name: review/$CI_COMMIT_REF_NAME action: stop resource_group: review/$CI_COMMIT_REF_NAME rules: # exclude merge requests - if: $CI_MERGE_REQUEST_ID when: never # exclude tags - if: $CI_COMMIT_TAG when: never # exclude if $CLEANUP_ALL_REVIEW set to 'force' - if: '$CLEANUP_ALL_REVIEW == "force"' when: never # only on non-production, non-integration branches, with $OS_REVIEW_PROJECT set - if: '$OS_REVIEW_PROJECT && $CI_COMMIT_REF_NAME !~ $PROD_REF && $CI_COMMIT_REF_NAME !~ $INTEG_REF' when: manual allow_failure: true # stop all review envs (manual job on master branch) os-cleanup-all-review: extends: .os-base stage: deploy dependencies: [] before_script: - *os-scripts - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}" - assert_defined "${OS_REVIEW_URL:-$OS_URL}" 'Missing required env $OS_REVIEW_URL or $OS_URL' - assert_defined "${OS_REVIEW_TOKEN:-$OS_TOKEN}" 'Missing required env $OS_REVIEW_TOKEN or $OS_TOKEN' - oc login ${OS_REVIEW_URL:-$OS_URL} --token=${OS_REVIEW_TOKEN:-$OS_TOKEN} -n $OS_REVIEW_PROJECT script: - delete_all review "${OS_REVIEW_APP_NAME:-${OS_BASE_APP_NAME}-review-.*}" rules: # exclude merge requests - if: $CI_MERGE_REQUEST_ID when: never # exclude tags - if: $CI_COMMIT_TAG when: never # exclude if $OS_REVIEW_PROJECT not set - if: '$OS_REVIEW_PROJECT == null' when: never # on any branch with $CLEANUP_ALL_REVIEW set to 'force': auto, always (doesn't depend on upstream pipeline succeeds) - if: '$CLEANUP_ALL_REVIEW == "force"' when: always allow_failure: true # on production: manual - if: '$CLEANUP_ALL_REVIEW && $CI_COMMIT_REF_NAME =~ $PROD_REF' when: manual allow_failure: true os-integration: extends: .os-deploy variables: ENV_TYPE: integration ENV_APP_NAME: "$OS_INTEG_APP_NAME" ENV_URL: "$OS_INTEG_URL" ENV_TOKEN: "$OS_INTEG_TOKEN" ENV_PROJECT: "$OS_INTEG_PROJECT" environment: name: integration url: "${OS_INTEG_ENVIRONMENT_URL}" resource_group: integration rules: # exclude merge requests - if: $CI_MERGE_REQUEST_ID when: never # only on integration branch(es), with $OS_INTEG_PROJECT set - if: '$OS_INTEG_PROJECT && $CI_COMMIT_REF_NAME =~ $INTEG_REF' # Staging deploys are disabled by default since # continuous deployment to production is enabled by default # If you prefer to automatically deploy to staging and # only manually promote to production, enable this job by setting # $OS_STAGING_PROJECT. os-staging: extends: .os-deploy variables: ENV_TYPE: staging ENV_APP_NAME: "$OS_STAGING_APP_NAME" ENV_URL: "$OS_STAGING_URL" ENV_TOKEN: "$OS_STAGING_TOKEN" ENV_PROJECT: "$OS_STAGING_PROJECT" environment: name: staging url: "${OS_STAGING_ENVIRONMENT_URL}" resource_group: staging rules: # exclude merge requests - if: $CI_MERGE_REQUEST_ID when: never # only on production branch(es), with $OS_STAGING_PROJECT set - if: '$OS_STAGING_PROJECT && $CI_COMMIT_REF_NAME =~ $PROD_REF' # Deploy to production if on branch master and variable OS_PROD_PROJECT defined and AUTODEPLOY_TO_PROD is set os-production: extends: .os-deploy stage: production variables: ENV_TYPE: production ENV_APP_SUFFIX: "" # no suffix for prod ENV_APP_NAME: "$OS_PROD_APP_NAME" ENV_URL: "$OS_PROD_URL" ENV_TOKEN: "$OS_PROD_TOKEN" ENV_PROJECT: "$OS_PROD_PROJECT" environment: name: production url: "${OS_PROD_ENVIRONMENT_URL}" resource_group: production rules: # exclude merge requests - if: $CI_MERGE_REQUEST_ID when: never # exclude non-production branches - if: '$CI_COMMIT_REF_NAME !~ $PROD_REF' when: never # exclude if $OS_PROD_PROJECT not set - if: '$OS_PROD_PROJECT == null || $OS_PROD_PROJECT == ""' when: never # if $AUTODEPLOY_TO_PROD: auto - if: $AUTODEPLOY_TO_PROD # else if PUBLISH_ON_PROD enabled: auto (because the publish job was blocking) - if: '$PUBLISH_ON_PROD == "true" || $PUBLISH_ON_PROD == "yes"' # else: manual, blocking - if: $OS_PROD_PROJECT # useless test, just to prevent GitLab warning when: manual