Hello!
There is a common CI/CD .gitlab-ci.yml
:
stages:
- ansible-lint
- stage
- production
include:
- local: '.cicd/ansible-lint.yml'
- local: '.cicd/stageMaster.yml'
rules:
- if: $CI_COMMIT_REF_SLUG == $CI_DEFAULT_BRANCH
- when: never
- local: '.cicd/stageFeature.yml'
rules:
- if: $CI_COMMIT_REF_SLUG != $CI_DEFAULT_BRANCH
- when: never
.ci cd/stage Feature.yml
:
.options_feature:
ENVIRONMENT_FEATURE:
value: "test"
options:
- "test"
- "dev"
description: "ENVIRONMENT для inventory, доступны test, dev. По умолчанию 'test'"
ROLE_FEATURE:
value: "N/A"
options:
- "N/A"
- "haproxy"
- "manticore"
- "nginx"
- "proxysql"
- "users"
- "xtradb"
description: "Ansible role для запуска"
ANSIBLE_HOSTS_FEATURE:
value: ""
description: "HOSTS для playbook. Может быть имя group/hostname (ansible_hostname). По умолчанию использует указанный в playbook hosts"
ANSIBLE_TAGS_FEATURE:
value: ""
description: "TAGS для playbook. Можно указать несколько 'tag1,tag2,tag3'. По умолчанию использует все в playbook (если не указаны исключения/условия)"
ANSIBLE_TAGS_EXCLUDE_FEATURE:
value: ""
description: "TAGS EXCLUDE для playbook, для исключения TASK с указанными TAG. Можно указать несколько 'tag1,tag2,tag3'"
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
RICH_VERSION: 13.7.1
PYTHON_IMAGE: python:3.12-slim
ANSIBLE_LINT_VERSION: 24.7.0
ANSIBLE_SECRETS_DIR: "./.secret"
ANSIBLE_SSHKEY_FILE: devops
ANSIBLE_VAULTKEY_FILE: .vault_pass
ANSIBLE_CFG_FILE: "./ansible.cfg"
extends: .options_feature
stages:
- stage
Ansible-playbook run feature:
image: $PYTHON_IMAGE
stage: stage
environment:
name: stage
cache:
paths:
- $PIP_CACHE_DIR
- venv/
variables:
ENVIRONMENT: $ENVIRONMENT_FEATURE
ROLE: $ROLE_FEATURE
ANSIBLE_HOSTS: $ANSIBLE_HOSTS_FEATURE
ANSIBLE_TAGS: $ANSIBLE_TAGS_FEATURE
ANSIBLE_TAGS_EXCLUDE: $ANSIBLE_TAGS_EXCLUDE_FEATURE
before_script:
- python --version
- |
# Install and activate virtualenv
pip3 install virtualenv
virtualenv venv
source venv/bin/activate
- |
# INSTALL PACKAGES
# pip3 install packages
pip3 install rich==$RICH_VERSION
pip3 install ansible
pip3 install ansible_lint==$ANSIBLE_LINT_VERSION
- |
# VERSIONS
ansible --version
ansible-lint --version
- |
# Install openssh-client and openssl
apt-get update -yqq
apt-get install -yqq openssh-client openssl
- |
# ANSIBLE PREPARING
mkdir $ANSIBLE_SECRETS_DIR
chmod 700 $ANSIBLE_SECRETS_DIR
if ! [[ -v ${ANSIBLE_SSHKEY} ]] && ! [[ -z ${ANSIBLE_SSHKEY} ]]; then
cat $ANSIBLE_SSHKEY > $ANSIBLE_SECRETS_DIR/$ANSIBLE_SSHKEY_FILE
chmod 600 $ANSIBLE_SECRETS_DIR/$ANSIBLE_SSHKEY_FILE
else
echo -e "\e[31mERROR: ANSIBLE_SSHKEY variable or value is not defined!"
exit 1
fi
if ! [[ -v ${ANSIBLE_VAULTKEY} ]] && ! [[ -z ${ANSIBLE_VAULTKEY} ]]; then
cat $ANSIBLE_VAULTKEY > $ANSIBLE_SECRETS_DIR/$ANSIBLE_VAULTKEY_FILE
chmod 400 $ANSIBLE_SECRETS_DIR/$ANSIBLE_VAULTKEY_FILE
else
echo -e "\e[31mERROR: ANSIBLE_VAULTKEY variable or value is not defined!"
exit 1
fi
if ! [[ -v ${ANSIBLE_CFG} ]] && ! [[ -z ${ANSIBLE_CFG} ]]; then
cat $ANSIBLE_CFG > $ANSIBLE_CFG_FILE
export ANSIBLE_CONFIG=$ANSIBLE_CFG_FILE
chmod 400 $ANSIBLE_CFG_FILE
else
echo -e "\e[31mERROR: ANSIBLE_CFG variable or value is not defined!"
exit 1
fi
# ssh-agent
eval $(ssh-agent)
ssh-add $ANSIBLE_SECRETS_DIR/$ANSIBLE_SSHKEY_FILE
script:
- |
# CHECK OPTIONS
if [ "$ROLE" = "N/A" ]; then
echo -e "\e[31mERROR: ROLE not selected from the list!"
exit 1
fi
echo "ENVIRONMENT: $ENVIRONMENT"
echo "ROLE: $ROLE"
echo "ANSIBLE_HOSTS: $ANSIBLE_HOSTS"
echo "ANSIBLE_TAGS: $ANSIBLE_TAGS"
echo "ANSIBLE_TAGS_EXCLUDE: $ANSIBLE_TAGS_EXCLUDE"
- |
# DEFINE ansible_args
ansible_args=()
if [ -n "$ANSIBLE_HOSTS" ]; then
ansible_args+=("-l" "$ANSIBLE_HOSTS")
fi
if [ -n "$ANSIBLE_TAGS" ]; then
ansible_args+=("--tags" "$ANSIBLE_TAGS")
fi
if [ -n "$ANSIBLE_TAGS_EXCLUDE" ]; then
ansible_args+=("--skip-tags" "$ANSIBLE_TAGS_EXCLUDE")
fi
echo "ansible_args: ${ansible_args[@]}"
- |
# OUTPUTS A LIST OF MATCHING HOSTS
ansible-playbook ./$ROLE.yml -i "env/$ENVIRONMENT/hosts" "${ansible_args[@]}" --list-hosts
- |
# PERFORM A SYNTAX CHECK ON THE PLAYBOOK
ansible-playbook ./$ROLE.yml -i "env/$ENVIRONMENT/hosts" "${ansible_args[@]}" --syntax-check
- |
# PLAYBOOK RUN
ansible-playbook ./$ROLE.yml -i "env/$ENVIRONMENT/hosts" "${ansible_args[@]}"
rules:
- if: $ENVIRONMENT_FEATURE && $ROLE_FEATURE != "N/A"
when: always
- when: never
.cicd/stageMaster.yml
:
.options_master:
ENVIRONMENT_MASTER:
value: "test"
options:
- "test"
- "dev"
- "prod"
description: "ENVIRONMENT для inventory, доступны test, dev и prod. По умолчанию 'test'"
ROLE_MASTER:
value: "N/A"
options:
- "N/A"
- "manticore"
- "nginx"
- "users"
description: "Ansible role для запуска"
ANSIBLE_HOSTS_MASTER:
value: ""
description: "HOSTS для playbook. Может быть имя group/hostname (ansible_hostname). По умолчанию использует указанный в playbook hosts"
ANSIBLE_TAGS_MASTER:
value: ""
description: "TAGS для playbook. Можно указать несколько 'tag1,tag2,tag3'. По умолчанию использует все в playbook (если не указаны исключения/условия)"
ANSIBLE_TAGS_EXCLUDE_MASTER:
value: ""
description: "TAGS EXCLUDE для playbook, для исключения TASK с указанными TAG. Можно указать несколько 'tag1,tag2,tag3'"
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
RICH_VERSION: 13.7.1
PYTHON_IMAGE: python:3.12-slim
ANSIBLE_LINT_VERSION: 24.7.0
ANSIBLE_SECRETS_DIR: "./.secret"
ANSIBLE_SSHKEY_FILE: devops
ANSIBLE_VAULTKEY_FILE: .vault_pass
ANSIBLE_CFG_FILE: "./ansible.cfg"
extends: .options_master
stages:
- production
Ansible-playbook run master:
image: $PYTHON_IMAGE
stage: production
environment:
name: production
cache:
paths:
- $PIP_CACHE_DIR
- venv/
variables:
ENVIRONMENT: $ENVIRONMENT_MASTER
ROLE: $ROLE_MASTER
ANSIBLE_HOSTS: $ANSIBLE_HOSTS_MASTER
ANSIBLE_TAGS: $ANSIBLE_TAGS_MASTER
ANSIBLE_TAGS_EXCLUDE: $ANSIBLE_TAGS_EXCLUDE_MASTER
before_script:
- python --version
- |
# Install and activate virtualenv
pip3 install virtualenv
virtualenv venv
source venv/bin/activate
- |
# INSTALL PACKAGES
# pip3 install packages
pip3 install rich==$RICH_VERSION
pip3 install ansible
pip3 install ansible_lint==$ANSIBLE_LINT_VERSION
- |
# VERSIONS
ansible --version
ansible-lint --version
- |
# Install openssh-client and openssl
apt-get update -yqq
apt-get install -yqq openssh-client openssl
- |
# ANSIBLE PREPARING
mkdir $ANSIBLE_SECRETS_DIR
chmod 700 $ANSIBLE_SECRETS_DIR
if ! [[ -v ${ANSIBLE_SSHKEY} ]] && ! [[ -z ${ANSIBLE_SSHKEY} ]]; then
cat $ANSIBLE_SSHKEY > $ANSIBLE_SECRETS_DIR/$ANSIBLE_SSHKEY_FILE
chmod 600 $ANSIBLE_SECRETS_DIR/$ANSIBLE_SSHKEY_FILE
else
echo -e "\e[31mERROR: ANSIBLE_SSHKEY variable or value is not defined!"
exit 1
fi
if ! [[ -v ${ANSIBLE_VAULTKEY} ]] && ! [[ -z ${ANSIBLE_VAULTKEY} ]]; then
cat $ANSIBLE_VAULTKEY > $ANSIBLE_SECRETS_DIR/$ANSIBLE_VAULTKEY_FILE
chmod 400 $ANSIBLE_SECRETS_DIR/$ANSIBLE_VAULTKEY_FILE
else
echo -e "\e[31mERROR: ANSIBLE_VAULTKEY variable or value is not defined!"
exit 1
fi
if ! [[ -v ${ANSIBLE_CFG} ]] && ! [[ -z ${ANSIBLE_CFG} ]]; then
cat $ANSIBLE_CFG > $ANSIBLE_CFG_FILE
export ANSIBLE_CONFIG=$ANSIBLE_CFG_FILE
chmod 400 $ANSIBLE_CFG_FILE
else
echo -e "\e[31mERROR: ANSIBLE_CFG variable or value is not defined!"
exit 1
fi
# ssh-agent
eval $(ssh-agent)
ssh-add $ANSIBLE_SECRETS_DIR/$ANSIBLE_SSHKEY_FILE
script:
- |
# CHECK OPTIONS
if [ "$ROLE" = "N/A" ]; then
echo -e "\e[31mERROR: ROLE not selected from the list!"
exit 1
fi
echo "ENVIRONMENT: $ENVIRONMENT"
echo "ROLE: $ROLE"
echo "ANSIBLE_HOSTS: $ANSIBLE_HOSTS"
echo "ANSIBLE_TAGS: $ANSIBLE_TAGS"
echo "ANSIBLE_TAGS_EXCLUDE: $ANSIBLE_TAGS_EXCLUDE"
- |
# DEFINE ansible_args
ansible_args=()
if [ -n "$ANSIBLE_HOSTS" ]; then
ansible_args+=("-l" "$ANSIBLE_HOSTS")
fi
if [ -n "$ANSIBLE_TAGS" ]; then
ansible_args+=("--tags" "$ANSIBLE_TAGS")
fi
if [ -n "$ANSIBLE_TAGS_EXCLUDE" ]; then
ansible_args+=("--skip-tags" "$ANSIBLE_TAGS_EXCLUDE")
fi
echo "ansible_args: ${ansible_args[@]}"
- |
# OUTPUTS A LIST OF MATCHING HOSTS
ansible-playbook ./$ROLE.yml -i "env/$ENVIRONMENT/hosts" "${ansible_args[@]}" --list-hosts
- |
# PERFORM A SYNTAX CHECK ON THE PLAYBOOK
ansible-playbook ./$ROLE.yml -i "env/$ENVIRONMENT/hosts" "${ansible_args[@]}" --syntax-check
- |
# PLAYBOOK RUN
ansible-playbook ./$ROLE.yml -i "env/$ENVIRONMENT/hosts" "${ansible_args[@]}"
rules:
- if: $ENVIRONMENT_MASTER && $ROLE_MASTER != "N/A"
when: always
- when: never
.cicd/stageFeature.yml and .cicd/stageMaster.yml are run only manually via Run pipeline with variable values selected (variables with options) and values specified for other variables (optional for the task).
Problem:
if you run the pipeline from the master branch via Run pipeline, then the variables from the .options_feature
block in the .cicd/stageFeature.yml
manifest are substituted correctly.
But if you run the pipeline from the master branch via Run pipeline, then the variables are substituted not from the .options_master
block of the .cicd/stageMaster.yml
manifest, but from the .options_feature
block of the .cicd/stageFeature.yml
manifest.
This is incorrect.
However, if you run the Pipeline Editor, everything is correct and without errors. In CI/CD, depending on the branch being launched, only the corresponding manifest is visible: stageFeature - if from a non-master branch and stageMaster - if launched from the master branch. Also, in Full configuration, the general pipeline is correct and all variables are correct.
On the master branch, you can run it only if you manually replace the variables and specify the desired values. But this is not the result that is needed.
GitLab version 17.1.
Tell me what the problem could be? Is it really possible to do this or is it a bug?