Commit 7a166b18 authored by Jordy BARRÉ's avatar Jordy BARRÉ Committed by Cédric OLIVIER
Browse files

feat: add helm diff job

parent 64ef8573
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -384,6 +384,19 @@ This job runs [Kube-Score](https://kube-score.com/) on the resources to be creat
| `kube-score-args` / `HELM_KUBE_SCORE_ARGS` | Arguments used by the helm-score job   | _none_ |
| `k8s-version` / `HELM_K8S_VERSION` | Kubernetes version (_so that `.Capabilities.KubeVersion.Version` can be correctly interpreted_).<br/> Expected format: `vX.YY` | _none_ |

### `helm-diff` job

This job uses the [helm-diff](https://github.com/databus23/helm-diff) plugin to show the differences between the current and new chart state before deployment.

It uses the following variables:

| Input / Variable | Description                              | Default value     |
| --------------------- | ---------------------------------------- | ----------------- |
| `diff-disabled` / `HELM_DIFF_DISABLED` | Set to `true` to disable Helm Diff before deployment (enabled by default) | `false` (enabled) |
| `diff-args` / `HELM_DIFF_ARGS` | The Helm [command with options](https://github.com/databus23/helm-diff#usage) to show the diff (_without dynamic arguments such as release name or chart_) | `diff upgrade --allow-unreleased --color` |

When enabled, this job lets you preview the changes that would be applied by Helm before actually deploying them.

### `helm-package` job

This job [packages](https://helm.sh/docs/helm/helm_package/) the Helm chart. It uses the following variables:
+16 −0
Original line number Diff line number Diff line
@@ -85,6 +85,7 @@
      "default": "hostname",
      "advanced": true
    }

  ],
  "features": [
    {
@@ -101,6 +102,21 @@
        }
      ]
    },
    
    {
      "id": "diff",
      "name": "Helm Diff",
      "description": "Show diff before deployment with [Helm Diff](https://github.com/databus23/helm-diff)",
      "disable_with": "HELM_DIFF_DISABLED",
      "variables": [
        {
          "name": "HELM_DIFF_ARGS",
          "description": "The Helm [command with options](https://github.com/databus23/helm-diff#usage) to show diff before deployment (_without dynamic arguments such as release name and chart_)",
          "default": "diff upgrade --allow-unreleased --color",
          "advanced": true
        }
      ]
    },
    {
      "id": "test",
      "name": "Helm Test",
+13 −0
Original line number Diff line number Diff line
@@ -52,6 +52,19 @@ variables:
  AWS_PROD_REGION: $[[ inputs.aws-prod-region ]]
  AWS_PROD_OIDC_ROLE_ARN: $[[ inputs.aws-prod-oidc-role-arn ]]

.helm-diff:
  services:
    - name: "$TBC_TRACKING_IMAGE"
      command: ["--service", "helm", "9.2.3"]
    - name: "$TBC_AWS_PROVIDER_IMAGE"
      alias: "aws-auth-provider"
  variables:
    #  have to be explicitly declared in the YAML to be exported to the service
    AWS_JWT: $AWS_JWT
  id_tokens:
    AWS_JWT:
      aud: "$AWS_OIDC_AUD"

.helm-deploy:
  services:
    - name: "$TBC_TRACKING_IMAGE"
+12 −0
Original line number Diff line number Diff line
@@ -93,6 +93,18 @@ variables:
      echo '[WARN] $GCP_JWT is not set: cannot setup Application Default Credentials (ADC) authentication'
    fi

.helm-diff:
  before_script:
    - !reference [.helm-scripts]
    - !reference [.helm-gcp-adc]
    - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}"
    - add_helm_repositories
    - setup_kubeconfig
    - helm_plugin_diff false
  id_tokens:
    GCP_JWT:
      aud: "$GCP_OIDC_AUD"

.helm-deploy:
  before_script:
    - !reference [.helm-scripts]
+248 −0
Original line number Diff line number Diff line
@@ -75,6 +75,13 @@ spec:
    test-args:
      description: The Helm [command with options](https://helm.sh/docs/helm/helm_test/) to perform acceptance test (_without dynamic arguments such as release name_)
      default: test
    diff-disabled:
      description: Disable Helm Diff
      type: boolean
      default: false
    diff-args:
      description: The Helm [command with options](https://github.com/databus23/helm-diff) to show diff before deployment (_without dynamic arguments such as release name and chart_)
      default: diff upgrade --allow-unreleased --color
    yamllint-disabled:
      description: Disable Yaml Lint
      type: boolean
@@ -304,6 +311,7 @@ variables:
  HELM_ENVIRONMENT_URL: $[[ inputs.environment-url ]]
  HELM_LINT_DISABLED: $[[ inputs.lint-disabled ]]
  HELM_TEST_ENABLED: $[[ inputs.test-enabled ]]
  HELM_DIFF_DISABLED: $[[ inputs.diff-disabled ]]
  HELM_YAMLLINT_DISABLED: $[[ inputs.yamllint-disabled ]]
  HELM_KUBE_SCORE_DISABLED: $[[ inputs.kube-score-disabled ]]
  HELM_KUBE_SCORE_ARGS: $[[ inputs.kube-score-args ]]
@@ -354,6 +362,7 @@ variables:
  HELM_DEPLOY_ARGS: $[[ inputs.deploy-args ]]
  HELM_DELETE_ARGS: $[[ inputs.delete-args ]]
  HELM_TEST_ARGS: $[[ inputs.test-args ]]
  HELM_DIFF_ARGS: $[[ inputs.diff-args ]]

  HELM_BASE_APP_NAME: $[[ inputs.base-app-name ]]
  HELM_REVIEW_AUTOSTOP_DURATION: $[[ inputs.review-autostop-duration ]]
@@ -585,6 +594,28 @@ stages:
    done
  }
  
  function maybe_install_packages() {
    if command -v apt-get > /dev/null
    then
      # Debian
      if ! dpkg --status "$@" > /dev/null
      then
        apt-get update
        apt-get install --no-install-recommends --yes --quiet "$@"
      fi
    elif command -v apk > /dev/null
    then
      # Alpine
      if ! apk info --installed "$@" > /dev/null
      then
        apk add --no-cache "$@"
      fi
    else
      log_error "... didn't find any supported package manager to install $*"
      exit 1
    fi
  }

  function setup_kubeconfig() {
    explicit_config=${ENV_KUBE_CONFIG:-${HELM_DEFAULT_KUBE_CONFIG}}
    if [[ -f "$explicit_config" ]]
@@ -666,6 +697,58 @@ stages:
    fi
  }

  # Install helm-diff plugin (compatible Helm 3 & 4)
  # usage: helm_plugin_diff [verify] [version]
  # - verify: true/false (only relevant for Helm 4+)
  # - version: explicit plugin version (omit or empty to install latest)
  function helm_plugin_diff() {
    plugin_name="diff"
    plugin_url="https://github.com/databus23/helm-diff"
    plugin_verify="${1:-false}"
    plugin_version="${2:-}"

    # Check and install git if necessary
    if ! command -v git > /dev/null; then
      log_info "--- git is not installed, attempting installation..."
      maybe_install_packages git
    fi

    log_info "--- checking Helm plugin: \e[32m${plugin_name}\e[0m"

    plugin_helm_major="$(helm version --short 2>/dev/null | sed 's/^v//' | cut -d. -f1)"

    plugin_verify_flag=""
    # Helm version support EOL : https://helm.sh/el/blog/helm-4-released/#helm-v3-support
    if echo "$plugin_helm_major" | awk '/^[0-9]+$/ {exit 0} {exit 1}' && [ "$plugin_helm_major" -ge 4 ]; then
      plugin_verify_flag="--verify=${plugin_verify}"
      log_info "--- helm v${plugin_helm_major} detected: plugin signature verification = \e[33;1m${plugin_verify}\e[0m (--verify flag used)"
    else
      log_info "--- helm v${plugin_helm_major} detected: signature verification not supported (--verify flag ignored)"
    fi

    if helm plugin list 2>/dev/null | awk -v n="$plugin_name" 'NR>1 && $1==n {found=1} END{exit !found}'; then
      log_info "--- plugin \e[32m${plugin_name}\e[0m already installed"
      return 0
    fi

    if [ -n "$plugin_version" ]; then
      log_info "--- installing plugin \e[32m${plugin_name}\e[0m (version \e[33;1m${plugin_version}\e[0m)"
    else
      log_info "--- installing plugin \e[32m${plugin_name}\e[0m (latest)"
    fi

    plugin_errors=$(mktemp)
    if helm ${TRACE+--debug} plugin install \
      ${plugin_verify_flag:+$plugin_verify_flag} \
      ${plugin_version:+--version "$plugin_version"} \
      "$plugin_url" 2>"$plugin_errors"; then
        log_info "--- plugin \e[32m${plugin_name}\e[0m installed"
    else
        log_warn "--- failed to install plugin \e[32m${plugin_name})\e[0m:\n$(sed 's/^/... /g' "$plugin_errors")"
        return 1
    fi
  }

  function tbc_envsubst() {
    awk '
      BEGIN {
@@ -824,6 +907,20 @@ stages:
    fi
    log_info "--- using \\e[32mpackage\\e[0m: \\e[33;1m${_pkg}\\e[0m"

    # Run helm diff before deploy unless disabled
    if [ "$HELM_DIFF_DISABLED" != "true" ]; then
      log_info "--- ensuring helm-diff plugin is installed"
      helm_plugin_diff false || {
        log_error "Helm diff plugin not installed, aborting deploy. Set HELM_DIFF_DISABLED=true to skip diff."
        exit 1
      }
      log_info "--- running helm diff before deploy (set HELM_DIFF_DISABLED=true to skip)"
      helm_diff || {
        log_error "Helm diff failed, aborting deploy. Set HELM_DIFF_DISABLED=true to skip diff."
        exit 1
      }
    fi

    # shellcheck disable=SC2086
    helm $helm_opts $HELM_DEPLOY_ARGS $environment_name $_pkg

@@ -894,6 +991,59 @@ stages:
    fi
  }

  # diff application 
  function helm_diff() {
    export environment_type=$ENV_TYPE
    export environment_name=${ENV_APP_NAME:-${HELM_BASE_APP_NAME}${ENV_APP_SUFFIX}}
    export values_files=$ENV_VALUES
    export kube_namespace=${ENV_NAMESPACE:-${KUBE_NAMESPACE}}

    log_info "--- \\e[32mdiff\\e[0m"
    log_info "--- \$kube_namespace: \\e[33;1m${kube_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)"

    helm_opts=${TRACE+--debug}

    # set the environment type (if value name is defined)
    if [ -n "$HELM_ENV_VALUE_NAME" ]; then
      helm_opts="$helm_opts --set ${HELM_ENV_VALUE_NAME}=${environment_type}"
    fi

    if [ -n "${HELM_COMMON_VALUES}" ]; then
      log_info "--- using \\e[32mcommon values\\e[0m file: \\e[33;1m${HELM_COMMON_VALUES}\\e[0m"
      TBC_ENVSUBST_ENCODING=jsonstr tbc_envsubst "$HELM_COMMON_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"
      TBC_ENVSUBST_ENCODING=jsonstr tbc_envsubst "$values_files" > 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_opts="$helm_opts --kubeconfig $CI_PROJECT_DIR/.kubeconfig"
    fi

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

    _pkg=${helm_package_file:-$HELM_DEPLOY_CHART}
    if [ -z "${_pkg}" ]; then
      log_error "No Chart to diff! Please use \\e[32m\$HELM_DEPLOY_CHART\\e[0m to diff a chart from a repository"
      log_error "Or check the provided variables to package your own chart!"
      exit 1
    fi
    log_info "--- using \\e[32mpackage\\e[0m: \\e[33;1m${_pkg}\\e[0m"

    # shellcheck disable=SC2086
    helm $helm_opts $HELM_DIFF_ARGS $environment_name $_pkg
  }

  # test application (and dependencies)
  # $environment_type and $environment_name are propagated by dotenv artifact
  function helm_test() {
@@ -1232,6 +1382,22 @@ helm-publish:
    - TBC_ENVSUBST_ENCODING=jsonstr tbc_envsubst "$ENV_VALUES" > generated-values-env.yml
    - helm template $helm_package ${HELM_K8S_VERSION:+--kube-version "$HELM_K8S_VERSION"} --values generated-values-common.yml --values generated-values-env.yml | kube-score score ${HELM_K8S_VERSION:+--kubernetes-version "$HELM_K8S_VERSION"} ${HELM_KUBE_SCORE_ARGS} -


.helm-diff:
  extends: .helm-deploy-base
  stage: package-test
  variables:
    ENV_APP_SUFFIX: "-$CI_ENVIRONMENT_SLUG"
  before_script:
    - !reference [.helm-scripts]
    - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}"
    - add_helm_repositories
    - setup_kubeconfig
    - helm_plugin_diff false
  script:
    - helm_diff
  resource_group: $CI_ENVIRONMENT_NAME

# Deploy job prototype
# Can be extended to define a concrete environment
#
@@ -1308,6 +1474,26 @@ helm-publish:
# ==================================================
# Env: review
# ==================================================
# show diff for review env (only for feature branches)
helm-diff-review:
  extends: .helm-diff
  variables:
    ENV_TYPE: review
    ENV_APP_NAME: "$HELM_REVIEW_APP_NAME"
    ENV_URL: "${HELM_REVIEW_ENVIRONMENT_URL}"
    ENV_KUBE_CONFIG: "$HELM_REVIEW_KUBE_CONFIG"
    ENV_NAMESPACE: "$HELM_REVIEW_NAMESPACE"
    ENV_VALUES: "$HELM_REVIEW_VALUES"
  environment:
    name: review/$CI_COMMIT_REF_NAME
  resource_group: review/$CI_COMMIT_REF_NAME
  rules:
    # Exclude if HELM_DIFF_DISABLED is true or HELM_REVIEW_ENABLED is not true or on tag
    - if: '$HELM_DIFF_DISABLED == "true" || $HELM_REVIEW_ENABLED != "true" || $CI_COMMIT_TAG'
      when: never
    # Only on non-production, non-integration branches
    - if: '$CI_COMMIT_REF_NAME !~ $PROD_REF && $CI_COMMIT_REF_NAME !~ $INTEG_REF'

helm-values-lint-review:
  extends: .helm-values-lint
  variables:
@@ -1416,6 +1602,26 @@ helm-test-review:
# ==================================================
# Env: integration
# ==================================================
# show diff for integration env
helm-diff-integration:
  extends: .helm-diff
  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
  resource_group: integration
  rules:
    # Exclude if HELM_DIFF_DISABLED is true or HELM_INTEG_ENABLED is not true
    - if: '$HELM_DIFF_DISABLED == "true" || $HELM_INTEG_ENABLED != "true"'
      when: never
    # Only on integration branch(es)
    - if: '$CI_COMMIT_REF_NAME =~ $INTEG_REF'

helm-values-lint-integration:
  extends: .helm-values-lint
  variables:
@@ -1525,6 +1731,26 @@ helm-test-integration:
# ==================================================
# Env: staging
# ==================================================
# show diff for staging env
helm-diff-staging:
  extends: .helm-diff
  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
  resource_group: staging
  rules:
    # Exclude if HELM_DIFF_DISABLED is true or HELM_STAGING_ENABLED is not true
    - if: '$HELM_DIFF_DISABLED == "true" || $HELM_STAGING_ENABLED != "true"'
      when: never
    # Only on production branch(es)
    - if: '$CI_COMMIT_REF_NAME =~ $PROD_REF'

helm-values-lint-staging:
  extends: .helm-values-lint
  variables:
@@ -1624,6 +1850,28 @@ helm-test-staging:
# ==================================================
# Env: production
# ==================================================
# show diff for production env
helm-diff-production:
  extends: .helm-diff
  variables:
    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
  resource_group: production
  rules:
    # Exclude non-production branches
    - if: '$CI_COMMIT_REF_NAME !~ $PROD_REF'
      when: never
    # Exclude if $HELM_DIFF_DISABLED is true or $HELM_PROD_ENABLED is not true
    - if: '$HELM_DIFF_DISABLED == "true" || $HELM_PROD_ENABLED != "true"'
      when: never

helm-values-lint-production:
  extends: .helm-values-lint
  variables: