Commit 7c3adcc6 authored by Pierre Smeyers's avatar Pierre Smeyers
Browse files

Multiple TBC features catchup + finalize migration as a CI/CD component

See merge request to-be-continuous/cloud-foundry!58
parents 14f19f5f 0b9a6a3a
Loading
Loading
Loading
Loading
+400 −356

File changed.

Preview size limit exceeded, changes collapsed.

+16 −16
Original line number Diff line number Diff line
@@ -92,7 +92,7 @@
        },
        {
          "name": "CF_REVIEW_APP_NAME",
          "description": "The application name for review env (only define if different from global)",
          "description": "The application name for review env (only define to override default)",
          "advanced": true
        },
        {
@@ -135,17 +135,17 @@
        {
          "name": "CF_REVIEW_URL",
          "type": "url",
          "description": "Cloud Foundry API url for review env (only define if different from global)",
          "description": "Cloud Foundry API url for review env (only define to override default)",
          "advanced": true
        },
        {
          "name": "CF_REVIEW_USER",
          "description": "Cloud Foundry API username for review env (only define if different from global)",
          "description": "Cloud Foundry API username for review env (only define to override default)",
          "secret": true
        },
        {
          "name": "CF_REVIEW_PASSWORD",
          "description": "Cloud Foundry API password for review env (only define if different from global)",
          "description": "Cloud Foundry API password for review env (only define to override default)",
          "secret": true
        },
        {
@@ -177,7 +177,7 @@
        },
        {
          "name": "CF_INTEG_APP_NAME",
          "description": "The application name for integration env (only define if different from global)",
          "description": "The application name for integration env (only define to override default)",
          "advanced": true
        },
        {
@@ -215,17 +215,17 @@
        {
          "name": "CF_INTEG_URL",
          "type": "url",
          "description": "Cloud Foundry API url for integration env (only define if different from global)",
          "description": "Cloud Foundry API url for integration env (only define to override default)",
          "advanced": true
        },
        {
          "name": "CF_INTEG_USER",
          "description": "Cloud Foundry API username for integration env (only define if different from global)",
          "description": "Cloud Foundry API username for integration env (only define to override default)",
          "secret": true
        },
        {
          "name": "CF_INTEG_PASSWORD",
          "description": "Cloud Foundry API password for integration env (only define if different from global)",
          "description": "Cloud Foundry API password for integration env (only define to override default)",
          "secret": true
        },
        {
@@ -251,7 +251,7 @@
        },
        {
          "name": "CF_STAGING_APP_NAME",
          "description": "The application name for staging env (only define if different from global)",
          "description": "The application name for staging env (only define to override default)",
          "advanced": true
        },
        {
@@ -289,17 +289,17 @@
        {
          "name": "CF_STAGING_URL",
          "type": "url",
          "description": "Cloud Foundry API url for staging env (only define if different from global)",
          "description": "Cloud Foundry API url for staging env (only define to override default)",
          "advanced": true
        },
        {
          "name": "CF_STAGING_USER",
          "description": "Cloud Foundry API username for staging env (only define if different from global)",
          "description": "Cloud Foundry API username for staging env (only define to override default)",
          "secret": true
        },
        {
          "name": "CF_STAGING_PASSWORD",
          "description": "Cloud Foundry API password for staging env (only define if different from global)",
          "description": "Cloud Foundry API password for staging env (only define to override default)",
          "secret": true
        },
        {
@@ -325,7 +325,7 @@
        },
        {
          "name": "CF_PROD_APP_NAME",
          "description": "The application name for production env (only define if different from global)",
          "description": "The application name for production env (only define to override default)",
          "advanced": true
        },
        {
@@ -371,17 +371,17 @@
        {
          "name": "CF_PROD_URL",
          "type": "url",
          "description": "Cloud Foundry API url for production env (only define if different from global)",
          "description": "Cloud Foundry API url for production env (only define to override default)",
          "advanced": true
        },
        {
          "name": "CF_PROD_USER",
          "description": "Cloud Foundry API username for production env (only define if different from global)",
          "description": "Cloud Foundry API username for production env (only define to override default)",
          "secret": true
        },
        {
          "name": "CF_PROD_PASSWORD",
          "description": "Cloud Foundry API password for production env (only define if different from global)",
          "description": "Cloud Foundry API password for production env (only define to override default)",
          "secret": true
        },
        {
+2.23 KiB (21 KiB)
Loading image diff...
+83 −64
Original line number Diff line number Diff line
@@ -540,14 +540,16 @@ stages:

  function generate_vars_file() {
    # 1: dynamic template variables
    echo -e "appname: $appname\\ntmpappname: $tmpappname\\nhostname: $hostname\\ndomain: $domain\\nenv: $env" > "$targetvarfile"
    echo -e "environment_name: $environment_name\\ntmp_environment_name: $tmp_environment_name\\nhostname: $hostname\\ndomain: $domain\\nenvironment_type: $environment_type" > "$targetvarfile"
    # backward compatibility
    echo -e "appname: $environment_name\\ntmpappname: $tmp_environment_name\\nenv: $environment_type" >> "$targetvarfile"

    # 2: maybe merge with vars file from project
    envvarfile="$CF_SCRIPTS_DIR/cf-vars-${env}.yml"
    envvarfile="$CF_SCRIPTS_DIR/cf-vars-${environment_type}.yml"
    globvarfile="$CF_SCRIPTS_DIR/cf-vars.yml"
    if [[ -f "${envvarfile}" ]]
    then
      log_info "--- variables file for env \\e[33;1m${env}\\e[0m (\\e[33;1m${envvarfile}\\e[0m) found"
      log_info "--- variables file for env \\e[33;1m${environment_type}\\e[0m (\\e[33;1m${envvarfile}\\e[0m) found"
      awkenvsubst < "$envvarfile" >> "$targetvarfile"
    elif [[ -f "${globvarfile}" ]]
    then
@@ -562,18 +564,19 @@ stages:

  function push_application() {
    log_info "--- \\e[32mcf push\\e[0m"
    log_info "--- tmpappname: \\e[33;1m${tmpappname}\\e[0m"
    log_info "--- appname: \\e[33;1m${appname}\\e[0m"
    log_info "--- hostname: \\e[33;1m${hostname}\\e[0m"
    log_info "--- routepath: \\e[33;1m${routepath:-(n/a)}\\e[0m"
    log_info "--- \$tmp_environment_name: \\e[33;1m${tmp_environment_name}\\e[0m"
    log_info "--- \$environment_name: \\e[33;1m${environment_name}\\e[0m"
    log_info "--- \$hostname: \\e[33;1m${hostname}\\e[0m"
    log_info "--- \$routepath: \\e[33;1m${routepath:-(n/a)}\\e[0m"
    log_info "--- manifest: \\e[33;1m${manifestfile}\\e[0m"
    cf_push_args=("$tmpappname" "--vars-file" "$targetvarfile")

    cf_push_args=("$tmp_environment_name" "--vars-file" "$targetvarfile")
    # shellcheck disable=SC2206
    cf_push_args+=($pushargs)

    # extract routes from manifest and replace vars
    routes_from_manifest=$(get_routes "$manifestfile")
    if [[ -n "$routes_from_manifest" && "$appname" == "$tmpappname" ]]
    if [[ -n "$routes_from_manifest" && "$environment_name" == "$tmp_environment_name" ]]
    then
        # routes are defined in the manifest AND this isn't a blue/green deployment: use routes from manifest
        log_info "--- using routes from manifest (\\e[33;1m${routes_from_manifest}\\e[0m)"
@@ -593,16 +596,16 @@ stages:
    fi
    cf_push_args+=("-f" "$manifestfile")

    if [[ "$appname" == "$tmpappname" && -n "$ENV_RETIRED_APP_SUFFIX" ]] && app_present "$appname"
    if [[ "$environment_name" == "$tmp_environment_name" && -n "$ENV_RETIRED_APP_SUFFIX" ]] && app_present "$environment_name"
    then

      # keep retired version for non blue/green deployment
      app_retired_name="${appname}-$ENV_RETIRED_APP_SUFFIX"
      app_retired_name="${environment_name}-$ENV_RETIRED_APP_SUFFIX"

      log_info "--- delete previous retired version \\e[33;1m${app_retired_name}\\e[0m..."
      cf delete "$app_retired_name" -f
      log_info "--- retiring current app (rename \\e[33;1m${appname}\\e[0m to \\e[33;1m${app_retired_name}\\e[0m)..."
      cf rename "$appname" "$app_retired_name"
      log_info "--- retiring current app (rename \\e[33;1m${environment_name}\\e[0m to \\e[33;1m${app_retired_name}\\e[0m)..."
      cf rename "$environment_name" "$app_retired_name"
      
      unmap_app_routes "$app_retired_name"
      cf stop "$app_retired_name"
@@ -617,7 +620,7 @@ stages:
      log_info "--- \\e[32mpre-start hook\\e[0m (\\e[33;1m${pre_start}\\e[0m) found: execute"
      chmod +x "$pre_start"
      "$pre_start"
      cf start "$tmpappname"
      cf start "$tmp_environment_name"
    else
      log_info "--- auto-start: \\e[94;1myes\\e[0m"

@@ -634,7 +637,7 @@ stages:
    if [[ "$map_route_required" ]]
    then
      log_info "--- map route (\\e[33;1m${hostname}.${domain}\\e[0m)"
      cf map-route "$tmpappname" "$domain" --hostname "$hostname" ${routepath:+--path "$routepath"}
      cf map-route "$tmp_environment_name" "$domain" --hostname "$hostname" ${routepath:+--path "$routepath"}
    fi
  }

@@ -678,11 +681,9 @@ stages:
  }

  function simple_deploy() {
    export env=$1
    # for backward compatibility
    export stage=$1
    export tmpappname=$2
    export appname=$2
    export environment_type=$1
    export tmp_environment_name=$2
    export environment_name=$2
    export hostname=$3
    export domain=$4
    if [[ "$5" = "/*" ]]
@@ -695,23 +696,30 @@ stages:
      export routepath="/$5"
    fi
    export pushargs=$6
    # 7th argument is explicit target appname (for blue/green)
    # 7th argument is explicit target environment_name (for blue/green)
    if [[ "$7" ]]
    then
      appname="$7"
      environment_name="$7"
    fi

    # backward compatibility
    export env=$environment_type
    export stage=$environment_type
    export appname=$environment_name

    # find manifest
    manifestfile=$(ls -1 "$CF_SCRIPTS_DIR/${CF_MANIFEST_BASENAME}-${env}.yml" 2>/dev/null || ls -1 "$CF_SCRIPTS_DIR/${CF_MANIFEST_BASENAME}.yml" 2>/dev/null || echo "")
    manifestfile=$(ls -1 "$CF_SCRIPTS_DIR/${CF_MANIFEST_BASENAME}-${environment_type}.yml" 2>/dev/null || ls -1 "$CF_SCRIPTS_DIR/${CF_MANIFEST_BASENAME}.yml" 2>/dev/null || echo "")
    if [[ -z "$manifestfile" ]]
    then
      log_error "Manifest not found, lookedup paths are:\n - \\e[33;1m$CF_SCRIPTS_DIR/${CF_MANIFEST_BASENAME}-${env}.yml\\e[0m\n - \\e[33;1m$CF_SCRIPTS_DIR/${CF_MANIFEST_BASENAME}.yml\\e[0m"
      log_error "Manifest not found, lookedup paths are:\n - \\e[33;1m$CF_SCRIPTS_DIR/${CF_MANIFEST_BASENAME}-${environment_type}.yml\\e[0m\n - \\e[33;1m$CF_SCRIPTS_DIR/${CF_MANIFEST_BASENAME}.yml\\e[0m"
      exit 1
    fi
    export manifestfile

    log_info "--- \\e[32mdeploy\\e[0m (env: \\e[33;1m${env}\\e[0m)"
    log_info "--- looking for CF scripts in directory: \\e[33;1m${CF_SCRIPTS_DIR}\\e[0m"
    log_info "--- \\e[32mdeploy\\e[0m"
    log_info "--- \$environment_type: \\e[33;1m${environment_type}\\e[0m"
    log_info "--- \$environment_name: \\e[33;1m${environment_name}\\e[0m"
    log_info "--- \$hostname: \\e[33;1m${hostname}\\e[0m"

    pre_push

@@ -738,16 +746,17 @@ stages:
  }

  function blue_green_deploy() {
    export env=$1
    # for backward compatibility
    export stage=$1
    export environment_type=$1
    app_target=$2
    hostname_target=$3
    domain_target=$4
    vroute_path=$5
    push_args=$6

    log_info "--- \\e[32mblue_green_deploy\\e[0m (env: \\e[33;1m${env}\\e[0m)"
    # for backward compatibility
    export stage=$environment_type

    log_info "--- \\e[32mblue_green_deploy\\e[0m"
    app_tmp=${app_target}-tmp
    hostname_tmp=${hostname_target}-tmp
    domain_tmp=$(default_domain)
@@ -762,7 +771,7 @@ stages:
      else
        # simple deployment
        log_info "app \\e[33;1m${app_target}\\e[0m not found: proceed with simple deployment"
        simple_deploy "$env" "$app_target" "$hostname_target" "$domain_target" "$vroute_path" "$push_args"
        simple_deploy "$environment_type" "$app_target" "$hostname_target" "$domain_target" "$vroute_path" "$push_args"
        return 0
      fi
    fi
@@ -773,7 +782,7 @@ stages:
    # try/catch-like pattern
    set -E
    trap 'blue_green_rollback "$app_tmp"' ERR
    simple_deploy "$env" "$app_tmp" "$hostname_tmp" "$domain_tmp" "$vroute_path" "$push_args" "$app_target"
    simple_deploy "$environment_type" "$app_tmp" "$hostname_tmp" "$domain_tmp" "$vroute_path" "$push_args" "$app_target"
    set +E

    # map target route(s) to "green"
@@ -784,7 +793,7 @@ stages:
        for route in $routes_from_manifest
        do
            # replace vars
            route=${route//((appname))/$app_target}
            route=${route//((environment_name))/$app_target}
            route=${route//((hostname))/$hostname_target}
            # extract hostname and domain
            route_host=$(echo "$route" | cut -d '.' -f1)
@@ -821,7 +830,6 @@ stages:
      cf rename "$app_target" "$app_retired_name"
      unmap_app_routes "$app_retired_name"
      cf stop "$app_retired_name"

    fi
    echo

@@ -833,28 +841,32 @@ stages:
    post_bluegreen
  }

  function deploy() {
    env=$1
  function cf_deploy() {
    environment_type=$ENV_TYPE
    # zero downtime
    zdt=$2
    appname=$3
    hostname=$4
    domain=${5:-$(default_domain)}
    routepath=${6}
    pushargs=${7}
    zdt=${ENV_ZERODOWNTIME:-false}
    environment_name=${ENV_APP_NAME:-${CF_BASE_APP_NAME}${ENV_APP_SUFFIX}}
    hostname=${ENV_HOST_NAME:-${CF_BASE_APP_NAME}${ENV_APP_SUFFIX}}
    domain=${ENV_DOMAIN:-${CF_DEFAULT_DOMAIN:-$(default_domain)}}
    routepath=${ENV_ROUTE_PATH:-${CF_DEFAULT_ROUTE_PATH}}
    pushargs="${ENV_PUSH_ARGS:-${CF_DEFAULT_PUSH_ARGS}}"

    # unset any upstream deployment env & artifacts
    rm -f cloudfoundry.env
    rm -f environment_url.txt

    if [[ "$zdt" = "true" ]] && [[ "$CF_ROLLING_STRATEGY" != "true" ]]
    then
      # blue/green deploy
      blue_green_deploy "$env" "$appname" "$hostname" "$domain" "$routepath" "$pushargs"
      blue_green_deploy "$environment_type" "$environment_name" "$hostname" "$domain" "$routepath" "$pushargs"
    else
      # simple deploy
      simple_deploy "$env" "$appname" "$hostname" "$domain" "$routepath" "$pushargs"
      simple_deploy "$environment_type" "$environment_name" "$hostname" "$domain" "$routepath" "$pushargs"
    fi

    # finally persist environment url
    echo "$CI_ENVIRONMENT_URL" > environment_url.txt
    echo -e "environment_type=$env\\nenvironment_name=$appname\\nenvironment_url=$CI_ENVIRONMENT_URL" > cloudfoundry.env
    echo -e "environment_type=$environment_type\\nenvironment_name=$environment_name\\nenvironment_url=$CI_ENVIRONMENT_URL" >> cloudfoundry.env
  }

  function pre_delete() {
@@ -883,39 +895,46 @@ stages:
    fi
  }

  function delete() {
    export env=$1
    # for backward compatibility
    export stage=$1
    export appname=$2
  function cf_delete() {
    export environment_type=$ENV_TYPE
    export environment_name=${ENV_APP_NAME:-${CF_BASE_APP_NAME}${ENV_APP_SUFFIX}}

    # backward compatibility
    export env=$environment_type
    export appname=$environment_name
    export stage=$environment_type

    log_info "--- \\e[32mdelete\\e[0m (env: ${env}, appname: ${appname})"
    log_info "--- \\e[32mdelete\\e[0m"
    log_info "--- \$environment_type: \\e[33;1m${environment_type}\\e[0m"
    log_info "--- \$environment_name: \\e[33;1m${environment_name}\\e[0m"

    pre_delete

    # delete app
    log_info "--- \\e[32mcf delete\\e[0m"
    cf delete "$appname" -f -r
    cf delete "$environment_name" -f -r

    post_delete
  }

  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")
    export environment_type=$1
    environment_nameproto=$2
    # make environment_name regex by replacing $CI_COMMIT_REF_SLUG with .*
    environment_nameregex=$(echo "$environment_nameproto" | sed -r "s/$CI_COMMIT_REF_SLUG/.*/g")
    # list apps | pick 1st column (name) | filter apps with name "$regex"
    matchingapps=$(cf apps | cut -d' ' -f1 | awk "/^$appnameregex/ {print \$1}")
    matchingapps=$(cf apps | cut -d' ' -f1 | awk "/^$environment_nameregex/ {print \$1}")
    matchingappscount=$(echo "$matchingapps" | 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${matchingappscount}\\e[0m apps found"
    log_info "--- \\e[32mdelete all\\e[0m"
    log_info "--- \$environment_type: \\e[33;1m${environment_type}\\e[0m"
    log_info "--- environment name matcher: \\e[33;1m${environment_nameregex}\\e[0m) (\\e[33;1m${matchingappscount}\\e[0m apps found)"

    rc=0
    for matchingapp in $matchingapps
    do
      echo -e "\\e[1;93m-------------------------------------------------------------------------------\\e[0m"
      if ! delete "$env" "$matchingapp"
      if ! delete "$environment_type" "$matchingapp"
      then
        log_warn "... failed deleting review app \\e[33;1m${matchingapp}\\e[0m (see logs)"
        rc=1
@@ -936,8 +955,8 @@ stages:
      do
        key=$(echo "$line" | cut -d: -f1)
        value=$(echo "$line" | cut -d: -f2- | sed 's/^[[:space:]]*//')
        # appname and hostname vars will be replace dynamically (necessary for bluegreen deployment)
        if [[ "$key" != "appname" ]] && [[ "$key" != "hostname" ]]; then
        # environment_name and hostname vars will be replace dynamically (necessary for bluegreen deployment)
        if [[ "$key" != "environment_name" ]] && [[ "$key" != "hostname" ]]; then
          routes=$(
            for r in ${routes}; do
              echo "$r" | sed -E "s/\(\(${key}\)\)/${value//\//\\/}/"
@@ -1192,10 +1211,10 @@ stages:
    - assert_defined "$ENV_SPACE" 'Missing required Cloud Foundry space'
    - cf login -a ${ENV_URL:-$CF_URL} -u ${ENV_USER:-$CF_USER} -p "${ENV_PASSWORD:-$CF_PASSWORD}" -o ${ENV_ORG:-$CF_ORG} -s $ENV_SPACE
  script:
    # use $CI_ENVIRONMENT_SLUG for appname to avoid service name constraints (<=50 chars)
    # use $CI_ENVIRONMENT_SLUG for environment_name to avoid service name constraints (<=50 chars)
    # use $CI_ENVIRONMENT_SLUG for hostname to avoid constraints (<=63 chars)
    - manage_services "create"
    - deploy "$ENV_TYPE" "${ENV_ZERODOWNTIME:-false}" "${ENV_APP_NAME:-${CF_BASE_APP_NAME}${ENV_APP_SUFFIX}}" "${ENV_HOST_NAME:-${CF_BASE_APP_NAME}${ENV_APP_SUFFIX}}" "${ENV_DOMAIN:-${CF_DEFAULT_DOMAIN}}" "${ENV_ROUTE_PATH:-${CF_DEFAULT_ROUTE_PATH}}" "${ENV_PUSH_ARGS:-${CF_DEFAULT_PUSH_ARGS}}"
    - cf_deploy
  artifacts:
    name: "$ENV_TYPE env url or cf logs for $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
    when: always
@@ -1233,7 +1252,7 @@ stages:
    - assert_defined "$ENV_SPACE" 'Missing required Cloud Foundry space'
    - cf login -a ${ENV_URL:-$CF_URL} -u ${ENV_USER:-$CF_USER} -p "${ENV_PASSWORD:-$CF_PASSWORD}" -o ${ENV_ORG:-$CF_ORG} -s $ENV_SPACE
  script:
    - delete "$ENV_TYPE" ${ENV_APP_NAME:-${CF_BASE_APP_NAME}${ENV_APP_SUFFIX}}
    - cf_delete
    - manage_services "delete"
  environment:
    action: stop
+5 −5
Original line number Diff line number Diff line
#!/usr/bin/env bash
if [[ -z "$appname" ]]
if [[ -z "$environment_name" ]]
then
  echo "[ERROR] appname env not passed"
  echo "[ERROR] environment_name env not passed"
  exit 1
fi

if [[ -z "$tmpappname" ]]
if [[ -z "$tmp_environment_name" ]]
then
  echo "[ERROR] tmpappname env not passed"
  echo "[ERROR] tmp_environment_name env not passed"
  exit 1
fi

echo "pre-push hook called for $appname/$tmpappname"
echo "pre-push hook called for $environment_name/$tmp_environment_name"
Loading