Commit c1a83581 authored by Pierre Smeyers's avatar Pierre Smeyers
Browse files

Merge branch 'feat/env-url-improvement' into 'master'

Simplify environments url management in deployment templates

See merge request to-be-continuous/helm!46
parents 61900bb8 8129ecd0
Loading
Loading
Loading
Loading
+147 −105

File changed.

Preview size limit exceeded, changes collapsed.

+38 −4
Original line number Diff line number Diff line
@@ -11,7 +11,12 @@
    },
    {
      "name": "HELM_CHART_DIR",
      "description": "The folder in which is stored the Helm chart",
      "description": "The folder where the Helm chart is located",
      "default": "."
    },
    {
      "name": "HELM_SCRIPTS_DIR",
      "description": "The folder where hook scripts are located",
      "default": "."
    },
    {
@@ -43,6 +48,11 @@
      "default": "$CI_PROJECT_NAME",
      "advanced": true
    },
    {
      "name": "HELM_ENVIRONMENT_URL",
      "type": "url",
      "description": "The default environments url _(only define for static environment URLs declaration)_\n\n_supports late variable expansion (ex: `https://%{environment_name}.helm.acme.com`)_"
    },
    {
      "name": "HELM_DEPLOY_ARGS",
      "description": "The Helm [command with options](https://helm.sh/docs/helm/helm_upgrade/) to deploy the application (_without dynamic arguments such as release name and chart_)",
@@ -63,13 +73,13 @@
    },
    {
      "name": "HELM_ENV_VALUE_NAME",
      "description": "The environment type variable set to helm",
      "default": "env",
      "description": "The environment type variable set to Helm",
      "default": "environment_type",
      "advanced": true
    },
    {
      "name": "HELM_HOSTNAME_VALUE_NAME",
      "description": "The hostname variable set to helm",
      "description": "The hostname variable set to Helm",
      "default": "hostname",
      "advanced": true
    },
@@ -257,6 +267,12 @@
          "default": "${HELM_BASE_APP_NAME}-${CI_ENVIRONMENT_SLUG}",
          "advanced": true
        },
        {
          "name": "HELM_REVIEW_ENVIRONMENT_URL",
          "type": "url",
          "description": "The review environments url _(only define for static environment URLs declaration and if different from default)_",
          "advanced": true
        },
        {
          "name": "HELM_REVIEW_VALUES",
          "description": "The Values file to use with `review` environment"
@@ -288,6 +304,12 @@
          "default": "${HELM_BASE_APP_NAME}-integration",
          "advanced": true
        },
        {
          "name": "HELM_INTEG_ENVIRONMENT_URL",
          "type": "url",
          "description": "The integration environment url _(only define for static environment URLs declaration and if different from default)_",
          "advanced": true
        },
        {
          "name": "HELM_INTEG_VALUES",
          "description": "The Values file to use with `integration` environment"
@@ -319,6 +341,12 @@
          "default": "${HELM_BASE_APP_NAME}-staging",
          "advanced": true
        },
        {
          "name": "HELM_STAGING_ENVIRONMENT_URL",
          "type": "url",
          "description": "The staging environment url _(only define for static environment URLs declaration and if different from default)_",
          "advanced": true
        },
        {
          "name": "HELM_STAGING_VALUES",
          "description": "The Values file to use with `staging` environment"
@@ -355,6 +383,12 @@
          "default": "${HELM_BASE_APP_NAME}",
          "advanced": true
        },
        {
          "name": "HELM_PROD_ENVIRONMENT_URL",
          "type": "url",
          "description": "The production environment url _(only define for static environment URLs declaration and if different from default)_",
          "advanced": true
        },
        {
          "name": "HELM_PROD_VALUES",
          "description": "The Values file to use with `production` environment"
+127 −45
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ variables:
  HELM_KUBE_SCORE_IMAGE: "zegl/kube-score:latest-helm3"

  HELM_CHART_DIR: "."
  HELM_SCRIPTS_DIR: "."
  HELM_PACKAGE_ARGS: "package --dependency-update"
  HELM_PUBLISH_URL: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/api/release/charts"
  HELM_PUBLISH_SNAPSHOT_URL: "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/helm/api/snapshot/charts"
@@ -63,7 +64,7 @@ variables:

  HELM_REPOS: "stable@https://charts.helm.sh/stable bitnami@https://charts.bitnami.com/bitnami"

  HELM_ENV_VALUE_NAME: env
  HELM_ENV_VALUE_NAME: environment_type
  HELM_HOSTNAME_VALUE_NAME: hostname

  # Will work with gitlab Kubernetes integration (per env variables)
@@ -363,47 +364,85 @@ stages:
  }

  function awkenvsubst() {
    awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);val=ENVIRON[var];gsub(/["\\]/,"\\\\&", val);gsub("\n", "\\n", val);gsub("\r", "\\r", val);gsub("[$]{"var"}",val)}}1'
    awk '{while(match($0,"[$%]{[^}]*}")) {g0=substr($0,RSTART,RLENGTH); val=ENVIRON[substr(g0,3,RLENGTH-3)]; gsub(/["\\]/,"\\\\&",val); gsub("\n","\\n",val);gsub("\r","\\r",val); gsub(g0,val)}}1'
  }

  function exec_hook() {
    if [[ ! -x "$1" ]] && ! chmod +x "$1"
    then
      log_warn "... could not make \\e[33;1m${1}\\e[0m executable: please do it (chmod +x)"
      # fallback technique
      sh "$1"
    else
      "$1"
    fi
  }

  # deploy application
  function deploy() {
    export env=$1
    export appname=$2
    # extract hostname from $CI_ENVIRONMENT_URL
    hostname=$(echo "$CI_ENVIRONMENT_URL" | awk -F[/:] '{print $4}')
    export hostname

    export environment_type=$1
    export environment_name=$2
    namespace=$3
    values_files=$4
    environment_url=$5

    log_info "--- \\e[32mdeploy\\e[0m (env: \\e[33;1m${env}\\e[0m)"
    log_info "--- appname: \\e[33;1m${appname}\\e[0m"
    log_info "--- env: \\e[33;1m${env}\\e[0m"
    log_info "--- hostname: \\e[33;1m${hostname}\\e[0m"
    # backwards compatibility
    export env=$environment_type
    export appname=$environment_name

    # variables expansion in $environment_url
    environment_url=$(echo "$environment_url" | awkenvsubst)
    export environment_url
    # extract hostname from $environment_url
    hostname=$(echo "$environment_url" | awk -F[/:] '{print $4}')
    export hostname

    log_info "--- \\e[32mdeploy\\e[0m"
    log_info "--- \$namespace: \\e[33;1m${namespace}\\e[0m"
    log_info "--- \$environment_type: \\e[33;1m${environment_type}\\e[0m (Helm variable '$HELM_ENV_VALUE_NAME')"
    log_info "--- \$environment_name: \\e[33;1m${environment_name}\\e[0m (used as release name)"
    log_info "--- \$hostname: \\e[33;1m${hostname}\\e[0m (Helm variable '$HELM_HOSTNAME_VALUE_NAME')"

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

    # maybe execute pre deploy script
    prescript="$HELM_SCRIPTS_DIR/helm-pre-deploy.sh"
    if [[ -f "$prescript" ]]; then
      log_info "--- \\e[32mpre-deploy hook\\e[0m (\\e[33;1m${prescript}\\e[0m) found: execute"
      exec_hook "$prescript"
    else
      log_info "--- \\e[32mpre-deploy hook\\e[0m (\\e[33;1m${prescript}\\e[0m) not found: skip"
    fi

    helm_opts=$(get_helm_config_opt)
    
    helm_opts="$helm_opts --set ${HELM_ENV_VALUE_NAME}=$environment_type"
    helm_opts="$helm_opts --set ${HELM_HOSTNAME_VALUE_NAME}=$hostname"
    # backward compatibility
    helm_opts="$helm_opts --set env=$environment_type"

    if [ -n "$HELM_COMMON_VALUES" ]; then
      log_info "--- using \\e[32mcommon values\\e[0m file: \\e[33;1m${HELM_COMMON_VALUES}\\e[0m"
      awkenvsubst < "$HELM_COMMON_VALUES" > generated-values-common.yml
      helm_values_opt="--values generated-values-common.yml"
      helm_opts="$helm_opts --values generated-values-common.yml"
    fi

    if [ -n "$values_files" ]; then
      log_info "--- using \\e[32mvalues\\e[0m file: \\e[33;1m${values_files}\\e[0m"
      awkenvsubst < "$values_files" > generated-values.yml
      helm_values_opt="$helm_values_opt --values generated-values.yml"
      helm_opts="$helm_opts --values generated-values.yml"
    fi

    if [ -f "$CI_PROJECT_DIR/.kubeconfig" ]; then
      log_info "--- using \\e[32mkubeconfig\\e[0m: \\e[33;1m$CI_PROJECT_DIR/.kubeconfig\\e[0m"
      helm_namespace_opt="--kubeconfig $CI_PROJECT_DIR/.kubeconfig"
      helm_opts="$helm_opts --kubeconfig $CI_PROJECT_DIR/.kubeconfig"
    fi

    if [ -n "$namespace" ]; then
      log_info "--- using \\e[32mnamespace\\e[0m: \\e[33;1m${namespace}\\e[0m"
      helm_namespace_opt="$helm_namespace_opt --namespace $namespace"
      helm_opts="$helm_opts --namespace $namespace"
    fi

    package=$(ls -1 ./helm_packages/*.tgz 2>/dev/null || echo "")
@@ -415,65 +454,104 @@ stages:
    fi
    log_info "--- using \\e[32mpackage\\e[0m: \\e[33;1m${package}\\e[0m"


    # shellcheck disable=SC2086
    helm ${TRACE+--debug} $helm_opts $helm_namespace_opt $helm_values_opt --set "${HELM_ENV_VALUE_NAME}=$env,${HELM_HOSTNAME_VALUE_NAME}=$hostname" $HELM_DEPLOY_ARGS $appname $package
    helm ${TRACE+--debug} $helm_opts $HELM_DEPLOY_ARGS $environment_name $package

    # finally persist environment url
    echo "$CI_ENVIRONMENT_URL" > environment_url.txt
    echo -e "environment_type=$env\\nenvironment_name=$appname\\nenvironment_url=$CI_ENVIRONMENT_URL" > helm.env
    # maybe execute post deploy script
    postscript="$HELM_SCRIPTS_DIR/helm-post-deploy.sh"
    if [[ -f "$postscript" ]]; then
      log_info "--- \\e[32mpost-deploy hook\\e[0m (\\e[33;1m${postscript}\\e[0m) found: execute"
      exec_hook "$postscript"
    else
      log_info "--- \\e[32mpost-deploy hook\\e[0m (\\e[33;1m${postscript}\\e[0m) not found: skip"
    fi

    # persist environment url
    if [[ -f environment_url.txt ]]
    then
      environment_url=$(cat environment_url.txt)
      export environment_url
      log_info "--- dynamic environment url found: (\\e[33;1m$environment_url\\e[0m)"
    else
      echo "$environment_url" > environment_url.txt
    fi
    echo -e "environment_type=$environment_type\\nenvironment_name=$environment_name\\nenvironment_url=$environment_url" > helm.env
  }

  # delete application (and dependencies)
  function delete() {
    export env=$1
    export appname=$2
    export environment_type=$1
    export environment_name=$2
    namespace=$3

    log_info "--- \\e[32mdelete\\e[0m (env: ${env})"
    log_info "--- appname: \\e[33;1m${appname}\\e[0m"
    log_info "--- env: \\e[33;1m${env}\\e[0m"
    # backwards compatibility
    export env=$environment_type
    export appname=$environment_name

    log_info "--- \\e[32mdelete"
    log_info "--- \$namespace: \\e[33;1m${namespace}\\e[0m"
    log_info "--- \$environment_type: \\e[33;1m${environment_type}\\e[0m"
    log_info "--- \$environment_name: \\e[33;1m${environment_name}\\e[0m (used as release name)"

    # maybe execute pre delete script
    prescript="$HELM_SCRIPTS_DIR/helm-pre-delete.sh"
    if [[ -f "$prescript" ]]; then
      log_info "--- \\e[32mpre-delete hook\\e[0m (\\e[33;1m${prescript}\\e[0m) found: execute"
      exec_hook "$prescript"
    else
      log_info "--- \\e[32mpre-delete hook\\e[0m (\\e[33;1m${prescript}\\e[0m) not found: skip"
    fi

    helm_opts=$(get_helm_config_opt)

    if [ -f "$CI_PROJECT_DIR/.kubeconfig" ]; then
      log_info "--- using \\e[32mkubeconfig\\e[0m: \\e[33;1m$CI_PROJECT_DIR/.kubeconfig\\e[0m"
      helm_namespace_opt="--kubeconfig $CI_PROJECT_DIR/.kubeconfig"
      helm_opts="$helm_opts --kubeconfig $CI_PROJECT_DIR/.kubeconfig"
    fi

    if [ -n "$namespace" ]; then
      log_info "--- using \\e[32mnamespace\\e[0m: \\e[33;1m${namespace}\\e[0m"
      helm_namespace_opt="--namespace $namespace"
      helm_opts="$helm_opts --namespace $namespace"
    fi

    # shellcheck disable=SC2086
    helm ${TRACE+--debug} $helm_opts $helm_namespace_opt $HELM_DELETE_ARGS $appname
    helm ${TRACE+--debug} $helm_opts $HELM_DELETE_ARGS $environment_name

    # maybe execute post delete script
    postscript="$HELM_SCRIPTS_DIR/helm-post-delete.sh"
    if [[ -f "$postscript" ]]; then
      log_info "--- \\e[32mpost-delete hook\\e[0m (\\e[33;1m${postscript}\\e[0m) found: execute"
      exec_hook "$postscript"
    else
      log_info "--- \\e[32mpost-delete hook\\e[0m (\\e[33;1m${postscript}\\e[0m) not found: skip"
    fi
  }

  # test application (and dependencies)
  function test() {
    export env=$1
    export appname=$2
    export environment_type=$1
    export environment_name=$2
    namespace=$3

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

    helm_opts=$(get_helm_config_opt)

    if [ -f "$CI_PROJECT_DIR/.kubeconfig" ]; then
      log_info "--- using \\e[32mkubeconfig\\e[0m: \\e[33;1m$CI_PROJECT_DIR/.kubeconfig\\e[0m"
      helm_namespace_opt="--kubeconfig $CI_PROJECT_DIR/.kubeconfig"
      helm_opts="$helm_opts --kubeconfig $CI_PROJECT_DIR/.kubeconfig"
    fi

    if [ -n "$namespace" ]; then
      log_info "--- using \\e[32mnamespace\\e[0m: \\e[33;1m${namespace}\\e[0m"
      helm_namespace_opt="--namespace $namespace"
      helm_opts="$helm_opts --namespace $namespace"
    fi

    # shellcheck disable=SC2086
    helm ${TRACE+--debug} $helm_opts $helm_namespace_opt $HELM_TEST_ARGS $appname
    helm ${TRACE+--debug} $helm_opts $HELM_TEST_ARGS $environment_name
  }

  function maybe_install_curl() {
@@ -650,7 +728,7 @@ helm-integration-score:
    - awkenvsubst < "$HELM_INTEG_VALUES" > generated-values-integration.yml
    - helm template $helm_package --values generated-values-common.yml --values generated-values-integration.yml | kube-score score ${HELM_KUBE_SCORE_ARGS} -
  rules:
    # exclude when $K8S_SCORE_DISABLED is set
    # exclude when $HELM_SCORE_DISABLED is set
    - if: '$HELM_KUBE_SCORE_DISABLED == "true"'
      when: never
    - if: '$HELM_INTEG_VALUES == null || $HELM_INTEG_VALUES == ""'
@@ -668,7 +746,7 @@ helm-staging-score:
    - awkenvsubst < "$HELM_STAGING_VALUES" > generated-values-staging.yml
    - helm template $helm_package --values generated-values-common.yml --values generated-values-staging.yml | kube-score score ${HELM_KUBE_SCORE_ARGS} -
  rules:
    # exclude when $K8S_SCORE_DISABLED is set
    # exclude when $HELM_SCORE_DISABLED is set
    - if: '$HELM_KUBE_SCORE_DISABLED == "true"'
      when: never
    - if: '$HELM_STAGING_VALUES == null || $HELM_STAGING_VALUES == ""'
@@ -683,7 +761,7 @@ helm-prod-score:
    - awkenvsubst < "$HELM_PROD_VALUES" > generated-values-prod.yml
    - helm template $helm_package --values generated-values-common.yml --values generated-values-prod.yml | kube-score score ${HELM_KUBE_SCORE_ARGS} -
  rules:
    # exclude when $K8S_SCORE_DISABLED is set
    # exclude when $HELM_SCORE_DISABLED is set
    - if: '$HELM_KUBE_SCORE_DISABLED == "true"'
      when: never
    - if: '$HELM_PROD_VALUES == null || $HELM_PROD_VALUES == ""'
@@ -776,6 +854,7 @@ helm-publish:
# @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 application url
# @arg ENV_KUBE_CONFIG: env-specific Kubeconfig
# @arg ENV_NAMESPACE : env-specific Kubernetes namespace
# @arg ENV_VALUES    : env-specific Helm values
@@ -791,7 +870,7 @@ helm-publish:
    - add_helm_repositories
    - setup_kubeconfig "${ENV_KUBE_CONFIG:-${HELM_DEFAULT_KUBE_CONFIG}}"
  script:
    - deploy $ENV_TYPE "${ENV_APP_NAME:-${HELM_BASE_APP_NAME}${ENV_APP_SUFFIX}}" "${ENV_NAMESPACE:-${KUBE_NAMESPACE}}" "$ENV_VALUES"
    - deploy $ENV_TYPE "${ENV_APP_NAME:-${HELM_BASE_APP_NAME}${ENV_APP_SUFFIX}}" "${ENV_NAMESPACE:-${KUBE_NAMESPACE}}" "$ENV_VALUES" "${ENV_URL:-${HELM_ENVIRONMENT_URL:-$ENV_URL_LEGACY}}"
  artifacts:
    name: "$ENV_TYPE env url for $CI_PROJECT_NAME on $CI_COMMIT_REF_SLUG"
    paths:
@@ -799,6 +878,8 @@ helm-publish:
    reports:
      dotenv: helm.env
  resource_group: $CI_ENVIRONMENT_NAME
  environment:
    url: "$environment_url" # can be either static or dynamic

# Cleanup job prototype
# Can be extended for each deletable environment
@@ -853,12 +934,13 @@ helm-review:
  variables:
    ENV_TYPE: review
    ENV_APP_NAME: "$HELM_REVIEW_APP_NAME"
    ENV_URL: "${HELM_REVIEW_ENVIRONMENT_URL}"
    ENV_URL_LEGACY: "${HELM_REVIEW_ENVIRONMENT_SCHEME}://${CI_PROJECT_NAME}-${CI_ENVIRONMENT_SLUG}.${HELM_REVIEW_ENVIRONMENT_DOMAIN}"
    ENV_KUBE_CONFIG: "$HELM_REVIEW_KUBE_CONFIG"
    ENV_NAMESPACE: "$HELM_REVIEW_NAMESPACE"
    ENV_VALUES: "$HELM_REVIEW_VALUES"
  environment:
    name: review/$CI_COMMIT_REF_NAME
    url: "${HELM_REVIEW_ENVIRONMENT_SCHEME}://${CI_PROJECT_NAME}-${CI_ENVIRONMENT_SLUG}.${HELM_REVIEW_ENVIRONMENT_DOMAIN}"
    on_stop: helm-cleanup-review
  resource_group: review/$CI_COMMIT_REF_NAME
  rules:
@@ -920,12 +1002,12 @@ helm-integration:
  variables:
    ENV_TYPE: integration
    ENV_APP_NAME: "$HELM_INTEG_APP_NAME"
    ENV_URL: "${HELM_INTEG_ENVIRONMENT_URL}"
    ENV_KUBE_CONFIG: "$HELM_INTEG_KUBE_CONFIG"
    ENV_NAMESPACE: "$HELM_INTEG_NAMESPACE"
    ENV_VALUES: "$HELM_INTEG_VALUES"
  environment:
    name: integration
    url: "${HELM_INTEG_ENVIRONMENT_URL}"
    on_stop: helm-cleanup-integration
  resource_group: integration
  rules:
@@ -986,12 +1068,12 @@ helm-staging:
  variables:
    ENV_TYPE: staging
    ENV_APP_NAME: "$HELM_STAGING_APP_NAME"
    ENV_URL: "${HELM_STAGING_ENVIRONMENT_URL}"
    ENV_KUBE_CONFIG: "$HELM_STAGING_KUBE_CONFIG"
    ENV_NAMESPACE: "$HELM_STAGING_NAMESPACE"
    ENV_VALUES: "$HELM_STAGING_VALUES"
  environment:
    name: staging
    url: "${HELM_STAGING_ENVIRONMENT_URL}"
    on_stop: helm-cleanup-staging
  resource_group: staging
  rules:
@@ -1052,12 +1134,12 @@ helm-production:
    ENV_TYPE: production
    ENV_APP_NAME: "$HELM_PROD_APP_NAME"
    ENV_APP_SUFFIX: ""
    ENV_URL: "${HELM_PROD_ENVIRONMENT_URL}"
    ENV_KUBE_CONFIG: "$HELM_PROD_KUBE_CONFIG"
    ENV_NAMESPACE: "$HELM_PROD_NAMESPACE"
    ENV_VALUES: "$HELM_PROD_VALUES"
  environment:
    name: production
    url: "${HELM_PROD_ENVIRONMENT_URL}"
  resource_group: production
  rules:
    # exclude non-production branches