Commit 2b7ba93e authored by Pierre Smeyers's avatar Pierre Smeyers
Browse files

Merge branch 'feat/support-parallel-matrix' into 'main'

Add parallel:matrix jobs support

See merge request to-be-continuous/openshift!71
parents eca6e05d b560fa4d
Loading
Loading
Loading
Loading
+48 −0
Original line number Diff line number Diff line
@@ -363,6 +363,43 @@ The **static way** can be implemented simply by setting the appropriate configur
To implement the **dynamic way**, your deployment script shall simply generate a `environment_url.txt` file in the working directory, containing only
the dynamically generated url. When detected by the template, it will use it as the newly deployed environment url.

### Multiple environments support

The OpenShift template allows deploying multiple environments in parallel. Use cases of this include:

- monorepo, where a single Git repository might host several separate deployable components or apps,
- multi-instances deployment of the same application.

This feature can be enabled using the [parallel matrix jobs](https://docs.gitlab.com/ee/ci/yaml/#parallelmatrix)
pattern at the `.os-env-base` job level (this is the top parent job of all deployment jobs).
Environments namespacing is ensured by the `OS_ENVIRONMENT_NAMESPACE` variable (must start with a slash `/`).

Here is the example of the `.gitlab-ci.yml` file for a project deploying both a frontend and a backend applications:

```yaml
.os-env-base:
  parallel:
    matrix:
      - OS_ENVIRONMENT_NAMESPACE: "/front"
        # OpenShift deployment scripts are located in the ./front/ directory
        OS_SCRIPTS_DIR: "front"
      - OS_ENVIRONMENT_NAMESPACE: "/back"
        # OpenShift deployment scripts are located in the ./back/ directory
        OS_SCRIPTS_DIR: "back"

# ⚠ on_stop must be unset when defining parallel:matrix environments
# see: https://gitlab.com/gitlab-org/gitlab/-/issues/332247
os-review:
  environment:
    on_stop: null
```

The above configuration will deploy 2 environments on each pipeline:

- on feature branches: `review/front/$CI_COMMIT_REF_NAME` and `review/back/$CI_COMMIT_REF_NAME`
- on the integration branch: `integration/front` and `integration/back`
- on the production branch: `staging/front` and `staging/back` (and finally `production/front` and `production/back`)

### Deployment output variables

Each deployment job produces _output variables_ that are propagated to downstream jobs (using [dotenv artifacts](https://docs.gitlab.com/ci/yaml/artifacts_reports/#artifactsreportsdotenv)):
@@ -375,6 +412,16 @@ Those variables may be freely used in downstream jobs (for instance to run accep

You may also add and propagate your own custom variables, by pushing them to the `openshift.out.env` file in your [deployment script or hook](#supported-deployment-methods).

> [!important]
> If [multiple environments](#multiple-environments-support) are configured, the output variables are prefixed with a
> sluggified value of the `OS_ENVIRONMENT_NAMESPACE` variable (stripped of punctuation characters and converted to lowercase):
>
> * `<namespace_slug>_environment_type`: set to the type of environment (`review`, `integration`, `staging` or `production`),
> * `<namespace_slug>_environment_name`: the application name (see below),
> * `<namespace_slug>_environment_url`: set to the environment URL (whether determined statically or dynamically).
>
> The output dotenv file will be `openshift.out.env.<namespace_slug>` instead, and the dynamic variable `${environment_namespace}` can be used in your scripts and manifests to access the contextual value of `<namespace_slug>`.

## Configuration reference

### Secrets management
@@ -402,6 +449,7 @@ The OpenShift template uses some global configuration used throughout all jobs.
| :lock: `OS_TOKEN`        | Default OpenShift API [token](#supported-authentication-methods) | **has to be defined** |
| `base-app-name` / `OS_BASE_APP_NAME` | Base application name                  | `$CI_PROJECT_NAME` ([see GitLab doc](https://docs.gitlab.com/ci/variables/predefined_variables/)) |
| `environment-url` / `OS_ENVIRONMENT_URL`    | Default environments url _(only define for static environment URLs declaration)_<br/>_supports late variable expansion (ex: `https://%{environment_name}.openshift.acme.com`)_ | _none_ |
| `environment-namespace` / `OS_ENVIRONMENT_NAMESPACE` | Extra [GitLab environments](https://docs.gitlab.com/ci/environments/) namespace _(only required when deploying [multiple environments](#multiple-environments-support))_<br/>:warning: must start with a slash `/` | _none_ |
| `scripts-dir` / `OS_SCRIPTS_DIR` | directory where OpenShift scripts (templates, hook scripts) are located | `.` _(root project dir)_ |
| `base-template-name` / `OS_BASE_TEMPLATE_NAME`  | Base OpenShift template name           | `openshift` |
| `app-label` / `OS_APP_LABEL` | The OpenShift [label](https://docs.openshift.com/container-platform/latest/applications/creating_applications/using-templates.html#templates-writing-labels_using-templates) set with the `$environment_name` [dynamic variable](#using-variables) value. _Advanced usage_ | `app` |
+5 −0
Original line number Diff line number Diff line
@@ -40,6 +40,11 @@
      "type": "url",
      "description": "The default environments url _(only define for static environment URLs declaration)_\n\n_supports late variable expansion (ex: `https://%{environment_name}.openshift.acme.com`)_"
    },
    {
      "name": "OS_ENVIRONMENT_NAMESPACE",
      "description": "Extra [GitLab environments](https://docs.gitlab.com/ci/environments/) namespace _(only required when deploying multiple environments)_\n\n:warning: must start with a slash `/`",
      "advanced": true
    },
    {
      "name": "OS_SCRIPTS_DIR",
      "description": "directory where OpenShift scripts (templates, hook scripts) are located",
+45 −37
Original line number Diff line number Diff line
@@ -34,6 +34,12 @@ spec:

        _supports late variable expansion (ex: `https://%{environment_name}.openshift.acme.com`)_
      default: ''
    environment-namespace:
      description: |-
        Extra [GitLab environments](https://docs.gitlab.com/ci/environments/) namespace _(only required when deploying multiple environments)_
        
        :warning: must start with a slash `/`
      default: ''
    scripts-dir:
      description: directory where OpenShift scripts (templates, hook scripts) are located
      default: .
@@ -171,6 +177,7 @@ variables:

  OS_URL: $[[ inputs.url ]]
  OS_ENVIRONMENT_URL: $[[ inputs.environment-url ]]
  OS_ENVIRONMENT_NAMESPACE: $[[ inputs.environment-namespace ]]
  OS_APP_LABEL: $[[ inputs.app-label ]]
  OS_ENV_LABEL: $[[ inputs.env-label ]]
  OS_REVIEW_PROJECT: $[[ inputs.review-project ]]
@@ -582,6 +589,8 @@ stages:
    # shellcheck disable=SC2086
    project=$(oc $OS_OC_EXTRA_OPTS project -q)
    export project
    environment_namespace=$(echo "$OS_ENVIRONMENT_NAMESPACE" | tr -d '[:punct:]' | tr '[:upper:]' '[:lower:]')
    export environment_namespace
    export environment_type=$ENV_TYPE
    export environment_name=${ENV_APP_NAME:-${OS_BASE_APP_NAME}${ENV_APP_SUFFIX}}
    environment_url=${ENV_URL:-$OS_ENVIRONMENT_URL}
@@ -610,7 +619,7 @@ stages:
    log_info "--- \$hostname: \\e[33;1m${hostname}\\e[0m"

    # unset any upstream deployment env & artifacts
    rm -f openshift.out.env
    rm -f openshift.out.env*
    rm -f environment_url.txt

    # maybe execute deploy script
@@ -647,8 +656,15 @@ stages:
    else
      echo "$environment_url" > environment_url.txt
    fi
    echo -e "environment_type=$environment_type\\nenvironment_name=$environment_name\\nenvironment_url=$environment_url" >> openshift.out.env
    chmod 644 environment_url.txt openshift.out.env
    # var prefix ('_' if namespace)
    prefix="${environment_namespace:+${environment_namespace}_}"
    dotenvfile="openshift.out.env${environment_namespace:+.${environment_namespace}}"
    {
      echo "${prefix}environment_type=${environment_type}"
      echo "${prefix}environment_name=${environment_name}"
      echo "${prefix}environment_url=${environment_url}"
    } >> "$dotenvfile"
    chmod 644 environment_url.txt "$dotenvfile"

    check_readiness
  }
@@ -840,26 +856,36 @@ stages:
  before_script:
    - !reference [.os-scripts]
    - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}"
    - assert_defined "${ENV_API_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 $OS_OC_EXTRA_OPTS login "${ENV_API_URL:-$OS_URL}" --token="${ENV_TOKEN:-$OS_TOKEN}" -n "$ENV_PROJECT"

# Deploy job prototype
# Env management 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 deployment url (static)
# @arg ENV_API_URL   : env-specific OpenShift API url
# @arg ENV_TOKEN     : env-specific OpenShift API token
# @arg ENV_PROJECT   : env-specific OpenShift project name
.os-deploy:
.os-env-base:
  extends: .os-base
  stage: deploy
  variables:
    ENV_APP_SUFFIX: "-$CI_ENVIRONMENT_SLUG"
  before_script:
    - !reference [.os-base, before_script]
    - assert_defined "${ENV_API_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 $OS_OC_EXTRA_OPTS login "${ENV_API_URL:-$OS_URL}" --token="${ENV_TOKEN:-$OS_TOKEN}" -n "$ENV_PROJECT"
  environment:
    name: ${ENV_TYPE}${OS_ENVIRONMENT_NAMESPACE}
  resource_group: ${ENV_TYPE}${OS_ENVIRONMENT_NAMESPACE}

# Deploy job prototype
# Can be extended for each deployable environment
# @arg ENV_URL       : env-specific deployment url (static)
.os-deploy:
  extends: .os-env-base
  script:
    - os_deploy
  artifacts:
@@ -867,26 +893,15 @@ stages:
    paths:
      - environment_url.txt
    reports:
      dotenv: openshift.out.env
      dotenv: openshift.out.env*
  environment:
    url: "$environment_url" # can be either static or dynamic

# 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_API_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
  extends: .os-env-base
  # force no dependencies
  dependencies: []
  variables:
    ENV_APP_SUFFIX: "-$CI_ENVIRONMENT_SLUG"
  script:
    - os_delete
  environment:
@@ -905,10 +920,12 @@ os-review:
    ENV_TOKEN: "$OS_REVIEW_TOKEN"
    ENV_PROJECT: "$OS_REVIEW_PROJECT"
  environment:
    name: review/$CI_COMMIT_REF_NAME
    name: ${ENV_TYPE}${OS_ENVIRONMENT_NAMESPACE}/${CI_COMMIT_REF_NAME}
    # ⚠ on_stop must be unset when defining parallel:matrix environments
    # see: https://gitlab.com/gitlab-org/gitlab/-/issues/332247
    on_stop: os-cleanup-review
    auto_stop_in: "$OS_REVIEW_AUTOSTOP_DURATION"
  resource_group: review/$CI_COMMIT_REF_NAME
  resource_group: ${ENV_TYPE}${OS_ENVIRONMENT_NAMESPACE}/${CI_COMMIT_REF_NAME}
  rules:
    # exclude tags
    - if: $CI_COMMIT_TAG
@@ -932,9 +949,9 @@ os-cleanup-review:
    ENV_TOKEN: "$OS_REVIEW_TOKEN"
    ENV_PROJECT: "$OS_REVIEW_PROJECT"
  environment:
    name: review/$CI_COMMIT_REF_NAME
    name: ${ENV_TYPE}${OS_ENVIRONMENT_NAMESPACE}/${CI_COMMIT_REF_NAME}
    action: stop
  resource_group: review/$CI_COMMIT_REF_NAME
  resource_group: ${ENV_TYPE}${OS_ENVIRONMENT_NAMESPACE}/${CI_COMMIT_REF_NAME}
  rules:
    # exclude tags
    - if: $CI_COMMIT_TAG
@@ -952,7 +969,7 @@ os-cleanup-review:

# stop all review envs (manual job on master branch)
os-cleanup-all-review:
  extends: .os-base
  extends: .os-env-base
  variables:
    ENV_TYPE: review
    ENV_APP_NAME: "$OS_REVIEW_APP_NAME"
@@ -988,9 +1005,6 @@ os-integration:
    ENV_API_URL: "$OS_INTEG_URL"
    ENV_TOKEN: "$OS_INTEG_TOKEN"
    ENV_PROJECT: "$OS_INTEG_PROJECT"
  environment:
    name: integration
  resource_group: integration
  rules:
    # exclude if $OS_INTEG_PROJECT not set
    - if: '$OS_INTEG_PROJECT == null || $OS_INTEG_PROJECT == ""'
@@ -1013,9 +1027,6 @@ os-staging:
    ENV_API_URL: "$OS_STAGING_URL"
    ENV_TOKEN: "$OS_STAGING_TOKEN"
    ENV_PROJECT: "$OS_STAGING_PROJECT"
  environment:
    name: staging
  resource_group: staging
  rules:
    # exclude if $OS_STAGING_PROJECT not set
    - if: '$OS_STAGING_PROJECT == null || $OS_STAGING_PROJECT == ""'
@@ -1034,9 +1045,6 @@ os-production:
    ENV_API_URL: "$OS_PROD_URL"
    ENV_TOKEN: "$OS_PROD_TOKEN"
    ENV_PROJECT: "$OS_PROD_PROJECT"
  environment:
    name: production
  resource_group: production
  rules:
    # exclude non-production branches
    - if: '$CI_COMMIT_REF_NAME !~ $PROD_REF'