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

feat: standardize env url management

parent 374fcc0a
Loading
Loading
Loading
Loading
+191 −133

File changed.

Preview size limit exceeded, changes collapsed.

+55 −0
Original line number Diff line number Diff line
@@ -15,6 +15,17 @@
      "default": ".",
      "advanced": true
    },
    {
      "name": "ANSIBLE_BASE_APP_NAME",
      "description": "Base application name",
      "default": "$CI_PROJECT_NAME",
      "advanced": true
    },
    {
      "name": "ANSIBLE_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}.acme.com`)_"
    },
    {
      "name": "ANSIBLE_VAULT_PASSWORD",
      "description": "The Ansible vault password used to decrypt vars",
@@ -95,6 +106,17 @@
      "name": "Review",
      "description": "Dynamic review environments for your topic branches (see GitLab [Review Apps](https://docs.gitlab.com/ee/ci/review_apps/))",
      "variables": [
        {
          "name": "ANSIBLE_REVIEW_APP_NAME",
          "description": "The application name for review env (only define if different from global)",
          "advanced": true
        },
        {
          "name": "ANSIBLE_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": "ANSIBLE_REVIEW_INVENTORY",
          "description": "The inventory for `review` env",
@@ -153,6 +175,17 @@
      "name": "Integration",
      "description": "A continuous-integration environment associated to your integration branch (`develop` by default)",
      "variables": [
        {
          "name": "ANSIBLE_INTEG_APP_NAME",
          "description": "The application name for integration env (only define if different from global)",
          "advanced": true
        },
        {
          "name": "ANSIBLE_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": "ANSIBLE_INTEG_INVENTORY",
          "description": "The inventory for `integration` env",
@@ -212,6 +245,17 @@
      "name": "Staging",
      "description": "An iso-prod environment meant for testing and validation purpose on your production branch (`master` by default)",
      "variables": [
        {
          "name": "ANSIBLE_STAGING_APP_NAME",
          "description": "The application name for staging env (only define if different from global)",
          "advanced": true
        },
        {
          "name": "ANSIBLE_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": "ANSIBLE_STAGING_INVENTORY",
          "description": "The inventory for `staging` env",
@@ -271,6 +315,17 @@
      "name": "Production",
      "description": "The production environment",
      "variables": [
        {
          "name": "ANSIBLE_PROD_APP_NAME",
          "description": "The application name for production env (only define if different from global)",
          "advanced": true
        },
        {
          "name": "ANSIBLE_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": "AUTODEPLOY_TO_PROD",
          "type": "boolean",
+62 −18
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ variables:

  # Docker Image with Ansible
  ANSIBLE_IMAGE: "cytopia/ansible:latest-tools"
  ANSIBLE_BASE_APP_NAME: "$CI_PROJECT_NAME"
  ANSIBLE_PROJECT_DIR: "."
  ANSIBLE_LINT_IMAGE: "haxorof/ansible-lint:latest"
  ANSIBLE_FORCE_COLOR: "true"
@@ -303,21 +304,32 @@ stages:
  }

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

  function run_ansible() {
    inventory=$1
    tags=$2
    extra_args=$3
    extra_opts=$3
    playbook_file=$4
    private_key=$5
    public_key=$6
    vault_password=$7

    # 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[32mrun_ansible\\e[0m"
    # shellcheck disable=SC2154
    log_info "--- \$environment_type: \\e[33;1m${environment_type}\\e[0m"
    # shellcheck disable=SC2154
    log_info "--- \$environment_name: \\e[33;1m${environment_name}\\e[0m"

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

@@ -347,10 +359,13 @@ stages:
      ansible-galaxy install -r "$ANSIBLE_REQUIREMENTS_FILE"
    fi

    # extra var environment_type & environment_name
    ansible_opts="-e environment_type=$environment_type -e environment_name=$environment_name"

    if [ -n "$vault_password" ]; then
      log_info "--- \\e[32mvault password\\e[0m found"
      echo "$vault_password" > .ansible_vault_password
      vault_opt="--vault-password-file .ansible_vault_password"
      ansible_opts="$ansible_opts --vault-password-file .ansible_vault_password"
    fi

    if [ -n "$private_key" ]; then
@@ -358,22 +373,22 @@ stages:
      if [ -f "$private_key" ]; then
        # chmod to prevent SSH client from complaining
        chmod 0600 "$private_key"
        private_key_opt="--private-key=$private_key -e ANSIBLE_SSH_PRIVATE_KEY_FILE=$private_key"
        ansible_opts="$ansible_opts --private-key=$private_key -e ssh_private_key_file=$private_key -e ANSIBLE_SSH_PRIVATE_KEY_FILE=$private_key"
      else
        echo "$private_key" > .ansible_private_key
        chmod 0600 .ansible_private_key
        private_key_opt="--private-key=.ansible_private_key -e ANSIBLE_SSH_PRIVATE_KEY_FILE=$(pwd)/.ansible_private_key"
        ansible_opts="$ansible_opts --private-key=.ansible_private_key -e ssh_private_key_file=.ansible_private_key -e ANSIBLE_SSH_PRIVATE_KEY_FILE=.ansible_private_key"
      fi
    fi

    if [ -n "$public_key" ]; then
      log_info "--- \\e[32mpublic key\\e[0m found"
      if [ -f "$public_key" ]; then
        public_key_opt="-e ANSIBLE_SSH_PUBLIC_KEY_FILE=$public_key"
        ansible_opts="$ansible_opts -e ssh_public_key_file=$public_key -e ANSIBLE_SSH_PUBLIC_KEY_FILE=$public_key"
      else
        echo "$public_key" > .ansible_public_key
        chmod 0600 .ansible_public_key
        public_key_opt="-e ANSIBLE_SSH_PUBLIC_KEY_FILE=$(pwd)/.ansible_public_key"
        ansible_opts="$ansible_opts -e ssh_public_key_file=.ansible_public_key -e ANSIBLE_SSH_PUBLIC_KEY_FILE=.ansible_public_key"
      fi
    fi

@@ -381,23 +396,23 @@ stages:

    if [ -n "$inventory" ]; then
      log_info "--- using \\e[32minventory\\e[0m file: \\e[33;1m${inventory}\\e[0m"
      inventory_opt="--inventory $inventory"
      ansible_opts="$ansible_opts --inventory $inventory"
    fi

    if [ -n "$tags" ]; then
      log_info "--- using \\e[32mtags\\e[0m list: \\e[33;1m${tags}\\e[0m"
      tags_opt="--tags $tags"
      ansible_opts="$ansible_opts --tags $tags"
    fi

    if [ -n "$extra_args" ]; then
      log_info "--- using \\e[32mextra args\\e[0m: \\e[33;1m${extra_args}\\e[0m"
    if [ -n "$extra_opts" ]; then
      log_info "--- using \\e[32mextra options\\e[0m: \\e[33;1m${extra_opts}\\e[0m"
    fi

    log_info "--- using \\e[32mansible-playbook\\e[0m version: \\n\\e[33;1m$(ansible-playbook --version)\\e[0m"
    log_info "--- run \\e[32mplaybook\\e[0m"

    # shellcheck disable=SC2086
    ansible-playbook $inventory_opt $private_key_opt $public_key_opt $tags_opt $vault_opt $extra_args "$playbook_file"
    ansible-playbook $ansible_opts $extra_opts "$playbook_file"

    # maybe execute post ansible-playbook script
    postscript="$ANSIBLE_SCRIPTS_DIR/post-ansible-playbook.sh"
@@ -411,19 +426,22 @@ stages:
      log_info "--- \\e[32mpost-ansible-playbook\\e[0m hook (\\e[33;1m${postscript}\\e[0m) not found: skip"
    fi

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

  function cleanup_secrets() {
@@ -548,6 +566,9 @@ ansible-lint-prod:
# 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 application url
# @arg ENV_INVENTORY     : env-specific Ansible inventory
# @arg ENV_PLAYBOOK_FILE : (mandatory) env-specific Ansible playbook
# @arg ENV_TAGS          : env-specific Ansible deploy tags
@@ -558,12 +579,17 @@ ansible-lint-prod:
.ansible-deploy:
  extends: .ansible-base
  stage: deploy
  variables:
    ENV_APP_SUFFIX: "-$CI_ENVIRONMENT_SLUG"
  before_script:
    - *ansible-scripts
    - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}"
    - cd $ANSIBLE_PROJECT_DIR
    - assert_defined "${ENV_INVENTORY:-${ANSIBLE_DEFAULT_INVENTORY}}" 'Missing required Ansible inventory'
    - assert_defined "${ENV_PLAYBOOK_FILE}" 'Missing required Ansible playbook'
    - export environment_type="$ENV_TYPE"
    - export environment_name="${ENV_APP_NAME:-${ANSIBLE_BASE_APP_NAME}${ENV_APP_SUFFIX}}"
    - export environment_url="${ENV_URL:-${ANSIBLE_ENVIRONMENT_URL:-$CI_ENVIRONMENT_URL}}"
    - chmod go-rwx .
  script:
    - !reference [ .ansible-commands, deploy ]
@@ -583,6 +609,8 @@ ansible-lint-prod:
# 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_INVENTORY     : env-specific Ansible inventory
# @arg ENV_CLEANUP_PLAYBOOK_FILE: env-specific Ansible playbook for cleanup (if different from deployment playbook)
# @arg ENV_PLAYBOOK_FILE : env-specific Ansible playbook
@@ -594,6 +622,8 @@ ansible-lint-prod:
.ansible-cleanup:
  extends: .ansible-base
  stage: deploy
  variables:
    ENV_APP_SUFFIX: "-$CI_ENVIRONMENT_SLUG"
  before_script:
    - *ansible-scripts
    - install_ca_certs "${CUSTOM_CA_CERTS:-$DEFAULT_CA_CERTS}"
@@ -601,6 +631,8 @@ ansible-lint-prod:
    - assert_defined "${ENV_INVENTORY:-${ANSIBLE_DEFAULT_INVENTORY}}" 'Missing required Ansible inventory'
    - assert_defined "${ENV_CLEANUP_PLAYBOOK_FILE:-${ENV_PLAYBOOK_FILE}}" 'Missing required Ansible playbook'
    - assert_defined "$ENV_CLEANUP_TAGS" 'Missing required Ansible cleanup tags'
    - export environment_type="$ENV_TYPE"
    - export environment_name="${ENV_APP_NAME:-${ANSIBLE_BASE_APP_NAME}${ENV_APP_SUFFIX}}"
    - chmod go-rwx .
  script:
    - !reference [ .ansible-commands, cleanup ]
@@ -617,6 +649,8 @@ ansible-review:
  extends: .ansible-deploy
  variables:
    ENV_TYPE: review
    ENV_APP_NAME: "$ANSIBLE_REVIEW_APP_NAME"
    ENV_URL: "${ANSIBLE_REVIEW_ENVIRONMENT_URL}"
    ENV_INVENTORY: "$ANSIBLE_REVIEW_INVENTORY"
    ENV_PLAYBOOK_FILE: "$ANSIBLE_REVIEW_PLAYBOOK_FILE"
    ENV_TAGS: "$ANSIBLE_REVIEW_TAGS"
@@ -640,6 +674,7 @@ ansible-cleanup-review:
  extends: .ansible-cleanup
  variables:
    ENV_TYPE: review
    ENV_APP_NAME: "$ANSIBLE_REVIEW_APP_NAME"
    ENV_INVENTORY: "$ANSIBLE_REVIEW_INVENTORY"
    ENV_PLAYBOOK_FILE: "$ANSIBLE_REVIEW_PLAYBOOK_FILE"
    ENV_CLEANUP_PLAYBOOK_FILE: "$ANSIBLE_REVIEW_CLEANUP_PLAYBOOK_FILE"
@@ -671,6 +706,8 @@ ansible-integration:
  extends: .ansible-deploy
  variables:
    ENV_TYPE: integration
    ENV_APP_NAME: "$ANSIBLE_INTEG_APP_NAME"
    ENV_URL: "${ANSIBLE_INTEG_ENVIRONMENT_URL}"
    ENV_INVENTORY: "$ANSIBLE_INTEG_INVENTORY"
    ENV_PLAYBOOK_FILE: "$ANSIBLE_INTEG_PLAYBOOK_FILE"
    ENV_TAGS: "$ANSIBLE_INTEG_TAGS"
@@ -692,6 +729,7 @@ ansible-cleanup-integration:
  extends: .ansible-cleanup
  variables:
    ENV_TYPE: integration
    ENV_APP_NAME: "$ANSIBLE_INTEG_APP_NAME"
    ENV_INVENTORY: "$ANSIBLE_INTEG_INVENTORY"
    ENV_PLAYBOOK_FILE: "$ANSIBLE_INTEG_PLAYBOOK_FILE"
    ENV_CLEANUP_PLAYBOOK_FILE: "$ANSIBLE_INTEG_CLEANUP_PLAYBOOK_FILE"
@@ -720,6 +758,8 @@ ansible-staging:
  extends: .ansible-deploy
  variables:
    ENV_TYPE: staging
    ENV_APP_NAME: "$ANSIBLE_STAGING_APP_NAME"
    ENV_URL: "${ANSIBLE_STAGING_ENVIRONMENT_URL}"
    ENV_INVENTORY: "$ANSIBLE_STAGING_INVENTORY"
    ENV_PLAYBOOK_FILE: "$ANSIBLE_STAGING_PLAYBOOK_FILE"
    ENV_TAGS: "$ANSIBLE_STAGING_TAGS"
@@ -741,6 +781,7 @@ ansible-cleanup-staging:
  extends: .ansible-cleanup
  variables:
    ENV_TYPE: staging
    ENV_APP_NAME: "$ANSIBLE_STAGING_APP_NAME"
    ENV_INVENTORY: "$ANSIBLE_STAGING_INVENTORY"
    ENV_PLAYBOOK_FILE: "$ANSIBLE_STAGING_PLAYBOOK_FILE"
    ENV_CLEANUP_PLAYBOOK_FILE: "$ANSIBLE_STAGING_CLEANUP_PLAYBOOK_FILE"
@@ -772,6 +813,9 @@ ansible-production:
  stage: production
  variables:
    ENV_TYPE: production
    ENV_APP_SUFFIX: "" # no suffix for prod
    ENV_APP_NAME: "$ANSIBLE_PROD_APP_NAME"
    ENV_URL: "${ANSIBLE_PROD_ENVIRONMENT_URL}"
    ENV_INVENTORY: "$ANSIBLE_PROD_INVENTORY"
    ENV_PLAYBOOK_FILE: "$ANSIBLE_PROD_PLAYBOOK_FILE"
    ENV_TAGS: "$ANSIBLE_PROD_TAGS"