This commit is contained in:
Work 2026-01-28 13:30:50 +03:00
parent 0b66c81c3f
commit 858fc2f232
7 changed files with 159 additions and 100 deletions

View File

@ -3,7 +3,7 @@ stages:
- trigger - trigger
variables: variables:
TERRAFORM_IMAGE_NAME: cr.yandex/crp3ccidau046kdj8g9q/terraform/terragrunt:v9.9 TERRAFORM_IMAGE_NAME: cr.yandex/crp3ccidau046kdj8g9q/terraform/terragrunt:v9.10
generate-pipeline: generate-pipeline:
stage: generate stage: generate
@ -11,10 +11,13 @@ generate-pipeline:
before_script: before_script:
- apk add --no-cache bash findutils - apk add --no-cache bash findutils
script: script:
- cd terraform
- chmod +x ./scripts/generate-pipeline.sh
- ./scripts/generate-pipeline.sh - ./scripts/generate-pipeline.sh
- cat .gitlab-ci.generated.yml
artifacts: artifacts:
paths: paths:
- .gitlab-ci.generated.yml - terraform/.gitlab-ci.generated.yml
expire_in: 1 hour expire_in: 1 hour
rules: rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
@ -27,17 +30,24 @@ trigger-downstream:
- generate-pipeline - generate-pipeline
trigger: trigger:
include: include:
- artifact: .gitlab-ci.generated.yml - artifact: terraform/.gitlab-ci.generated.yml
job: generate-pipeline job: generate-pipeline
strategy: depend
variables: variables:
TERRAFORM_IMAGE_NAME: ${TERRAFORM_IMAGE_NAME}
YC_IAM_TOKEN: ${YC_IAM_TOKEN} YC_IAM_TOKEN: ${YC_IAM_TOKEN}
YC_TOKEN: ${YC_IAM_TOKEN} YC_TOKEN: ${YC_IAM_TOKEN}
YC_CLOUD_ID: ${YC_CLOUD_ID}
YC_STAGE_FOLDER_ID: ${YC_STAGE_FOLDER_ID} YC_STAGE_FOLDER_ID: ${YC_STAGE_FOLDER_ID}
YC_PROD_FOLDER_ID: ${YC_PROD_FOLDER_ID}
YC_PREPROD_FOLDER_ID: ${YC_PREPROD_FOLDER_ID}
S3_ACCESS_KEY: ${S3_ACCESS_KEY} S3_ACCESS_KEY: ${S3_ACCESS_KEY}
S3_SECRET_KEY: ${S3_SECRET_KEY} S3_SECRET_KEY: ${S3_SECRET_KEY}
S3_STATE_BUCKET: ${S3_STATE_BUCKET} S3_STATE_BUCKET: ${S3_STATE_BUCKET}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} AWS_ACCESS_KEY_ID: ${S3_ACCESS_KEY}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} AWS_SECRET_ACCESS_KEY: ${S3_SECRET_KEY}
KUBECONFIG: ${KUBECONFIG}
KUBE_CONTEXT: ${KUBE_CONTEXT}
rules: rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH == "master" || $CI_COMMIT_BRANCH == "main"' - if: '$CI_COMMIT_BRANCH == "master" || $CI_COMMIT_BRANCH == "main"'

View File

@ -1,17 +1,20 @@
FROM alpine:latest FROM alpine:latest
RUN apk add --no-cache curl unzip bash git RUN apk add --no-cache curl unzip bash git jq
# Terraform
ARG TERRAFORM_VERSION=1.5.7 ARG TERRAFORM_VERSION=1.5.7
RUN curl -fsSL https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip -o terraform.zip && \ RUN curl -fsSL https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip -o terraform.zip && \
unzip terraform.zip -d /usr/local/bin/ && \ unzip terraform.zip -d /usr/local/bin/ && \
chmod +x /usr/local/bin/terraform && \ chmod +x /usr/local/bin/terraform && \
rm -f terraform.zip rm -f terraform.zip
# Terragrunt
ARG TERRAGRUNT_VERSION=0.93.11 ARG TERRAGRUNT_VERSION=0.93.11
RUN curl -fsSL https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/terragrunt_linux_amd64 -o /usr/local/bin/terragrunt && \ RUN curl -fsSL https://github.com/gruntwork-io/terragrunt/releases/download/v${TERRAGRUNT_VERSION}/terragrunt_linux_amd64 -o /usr/local/bin/terragrunt && \
chmod +x /usr/local/bin/terragrunt chmod +x /usr/local/bin/terragrunt
# Plugin cache и mirror для Yandex Cloud
RUN mkdir -p /root/.terraform.d/plugin-cache && \ RUN mkdir -p /root/.terraform.d/plugin-cache && \
mkdir -p /root/.terraform.d && \ mkdir -p /root/.terraform.d && \
echo 'provider_installation {' > /root/.terraformrc && \ echo 'provider_installation {' > /root/.terraformrc && \
@ -24,8 +27,10 @@ RUN mkdir -p /root/.terraform.d/plugin-cache && \
echo ' }' >> /root/.terraformrc && \ echo ' }' >> /root/.terraformrc && \
echo '}' >> /root/.terraformrc echo '}' >> /root/.terraformrc
# Environment variables
ENV TF_PLUGIN_CACHE_DIR=/root/.terraform.d/plugin-cache ENV TF_PLUGIN_CACHE_DIR=/root/.terraform.d/plugin-cache
ENV TG_TF_PATH=/usr/local/bin/terraform ENV TG_TF_PATH=/usr/local/bin/terraform
ENV AWS_EC2_METADATA_DISABLED=true
ENV TF_CLI_ARGS="-no-color"
WORKDIR /workspace WORKDIR /workspace

26
live/stage/database/.terraform.lock.hcl generated Normal file
View File

@ -0,0 +1,26 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/hashicorp/kubernetes" {
version = "2.38.0"
constraints = "~> 2.23"
hashes = [
"h1:ems+O2dA7atxMWpbtqIrsH7Oa+u+ERWSfpMaFnZPbh0=",
]
}
provider "registry.opentofu.org/hashicorp/random" {
version = "3.8.1"
constraints = "~> 3.1"
hashes = [
"h1:LsYuJLZcYl1RiH7Hd3w90Ra5+k5cNqfdRUQXItkTI8Y=",
]
}
provider "registry.opentofu.org/yandex-cloud/yandex" {
version = "0.127.0"
constraints = "~> 0.100"
hashes = [
"h1:tms24VWADoryHdkGdH5A76fWG2k+9CrE9Je9yZMXUoI=",
]
}

26
live/stage/namespace/.terraform.lock.hcl generated Normal file
View File

@ -0,0 +1,26 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/hashicorp/kubernetes" {
version = "2.38.0"
constraints = "~> 2.23"
hashes = [
"h1:ems+O2dA7atxMWpbtqIrsH7Oa+u+ERWSfpMaFnZPbh0=",
]
}
provider "registry.opentofu.org/hashicorp/random" {
version = "3.8.1"
constraints = "~> 3.1"
hashes = [
"h1:LsYuJLZcYl1RiH7Hd3w90Ra5+k5cNqfdRUQXItkTI8Y=",
]
}
provider "registry.opentofu.org/yandex-cloud/yandex" {
version = "0.127.0"
constraints = "~> 0.100"
hashes = [
"h1:tms24VWADoryHdkGdH5A76fWG2k+9CrE9Je9yZMXUoI=",
]
}

26
live/stage/s3/.terraform.lock.hcl generated Normal file
View File

@ -0,0 +1,26 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/hashicorp/kubernetes" {
version = "2.38.0"
constraints = "~> 2.23"
hashes = [
"h1:ems+O2dA7atxMWpbtqIrsH7Oa+u+ERWSfpMaFnZPbh0=",
]
}
provider "registry.opentofu.org/hashicorp/random" {
version = "3.8.1"
constraints = "~> 3.1"
hashes = [
"h1:LsYuJLZcYl1RiH7Hd3w90Ra5+k5cNqfdRUQXItkTI8Y=",
]
}
provider "registry.opentofu.org/yandex-cloud/yandex" {
version = "0.127.0"
constraints = "~> 0.100"
hashes = [
"h1:tms24VWADoryHdkGdH5A76fWG2k+9CrE9Je9yZMXUoI=",
]
}

View File

@ -14,7 +14,7 @@ remote_state {
skip_region_validation = true skip_region_validation = true
skip_credentials_validation = true skip_credentials_validation = true
skip_metadata_api_check = true skip_metadata_api_check = true
use_path_style = true force_path_style = true
skip_bucket_root_access = true skip_bucket_root_access = true
} }
} }

View File

@ -3,12 +3,12 @@ set -e
OUTPUT_FILE=".gitlab-ci.generated.yml" OUTPUT_FILE=".gitlab-ci.generated.yml"
yaml_escape() { # Определяем базовую директорию terraform (где находится этот скрипт)
echo "$1" | sed 's/"/\\"/g' SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
} TERRAFORM_DIR="$(dirname "$SCRIPT_DIR")"
TERRAFORM_PREFIX="terraform"
cat > "$OUTPUT_FILE" << 'EOF'
cat > "$OUTPUT_FILE" << 'HEADER'
workflow: workflow:
rules: rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
@ -21,27 +21,30 @@ variables:
TERRAFORM_IMAGE_NAME: ${TERRAFORM_IMAGE_NAME} TERRAFORM_IMAGE_NAME: ${TERRAFORM_IMAGE_NAME}
YC_IAM_TOKEN: ${YC_IAM_TOKEN} YC_IAM_TOKEN: ${YC_IAM_TOKEN}
YC_TOKEN: ${YC_IAM_TOKEN} YC_TOKEN: ${YC_IAM_TOKEN}
YC_CLOUD_ID: ${YC_CLOUD_ID}
YC_STAGE_FOLDER_ID: ${YC_STAGE_FOLDER_ID} YC_STAGE_FOLDER_ID: ${YC_STAGE_FOLDER_ID}
YC_PROD_FOLDER_ID: ${YC_PROD_FOLDER_ID} YC_PROD_FOLDER_ID: ${YC_PROD_FOLDER_ID}
YC_PREPROD_FOLDER_ID: ${YC_PREPROD_FOLDER_ID} YC_PREPROD_FOLDER_ID: ${YC_PREPROD_FOLDER_ID}
S3_ACCESS_KEY: ${S3_ACCESS_KEY} S3_ACCESS_KEY: ${S3_ACCESS_KEY}
S3_SECRET_KEY: ${S3_SECRET_KEY} S3_SECRET_KEY: ${S3_SECRET_KEY}
S3_STATE_BUCKET: ${S3_STATE_BUCKET} S3_STATE_BUCKET: ${S3_STATE_BUCKET}
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID} AWS_ACCESS_KEY_ID: ${S3_ACCESS_KEY}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY} AWS_SECRET_ACCESS_KEY: ${S3_SECRET_KEY}
AWS_EC2_METADATA_DISABLED: "true"
TF_CLI_ARGS: "-no-color"
TG_LOG_LEVEL: "error"
stages: stages:
- validate - validate
- plan - plan
- apply - apply
EOF HEADER
find live -name "terragrunt.hcl" -not -path "*/.terragrunt-cache/*" | sort | while read -r config_file; do find live -name "terragrunt.hcl" -not -path "*/.terragrunt-cache/*" | sort | while read -r config_file; do
component_dir=$(dirname "$config_file") component_dir=$(dirname "$config_file")
relative_path=$(echo "$component_dir" | sed 's|^live/||') relative_path=$(echo "$component_dir" | sed 's|^live/||')
# live/{env}/{component}/terragrunt.hcl
env=$(echo "$relative_path" | cut -d'/' -f1) env=$(echo "$relative_path" | cut -d'/' -f1)
component=$(echo "$relative_path" | cut -d'/' -f2) component=$(echo "$relative_path" | cut -d'/' -f2)
@ -51,55 +54,41 @@ find live -name "terragrunt.hcl" -not -path "*/.terragrunt-cache/*" | sort | whi
job_prefix="${env}-${component}" job_prefix="${env}-${component}"
# Полный путь от корня репо
full_component_dir="${TERRAFORM_PREFIX}/${component_dir}"
needs_deps="" needs_section=""
if [ "$component" = "secrets" ]; then if [ "$component" = "secrets" ]; then
needs_deps="validate-${env}-namespace validate-${env}-s3 validate-${env}-database" needs_section=" needs:
- validate-${env}-namespace
- validate-${env}-s3
- validate-${env}-database
dependencies:
- validate-${env}-namespace
- validate-${env}-s3
- validate-${env}-database"
fi fi
case "$env" in case "$env" in
stage) stage) folder_var="YC_STAGE_FOLDER_ID" ;;
folder_var="YC_STAGE_FOLDER_ID" prod) folder_var="YC_PROD_FOLDER_ID" ;;
;; preprod) folder_var="YC_PREPROD_FOLDER_ID" ;;
prod) *) folder_var="YC_FOLDER_ID" ;;
folder_var="YC_PROD_FOLDER_ID"
;;
preprod)
folder_var="YC_PREPROD_FOLDER_ID"
;;
*)
folder_var="YC_FOLDER_ID"
;;
esac esac
cat >> "$OUTPUT_FILE" << EOF cat >> "$OUTPUT_FILE" << VALIDATE_JOB
validate-${job_prefix}: validate-${job_prefix}:
stage: validate stage: validate
image: \${TERRAFORM_IMAGE_NAME} image: \${TERRAFORM_IMAGE_NAME}
variables: variables:
TG_ROOT: "${component_dir}" TG_ROOT: "${full_component_dir}"
ENVIRONMENT: "${env}" ENVIRONMENT: "${env}"
YC_FOLDER_ID: "\${${folder_var}}" YC_FOLDER_ID: "\${${folder_var}}"
YC_TOKEN: "\${YC_IAM_TOKEN}"
S3_ACCESS_KEY: "\${S3_ACCESS_KEY}"
S3_SECRET_KEY: "\${S3_SECRET_KEY}"
S3_STATE_BUCKET: "\${S3_STATE_BUCKET}"
TG_TF_PATH: "/usr/local/bin/terraform"
AWS_ACCESS_KEY_ID: "\${AWS_ACCESS_KEY_ID}"
AWS_SECRET_ACCESS_KEY: "\${AWS_SECRET_ACCESS_KEY}"
before_script: before_script:
- echo "TG_ROOT=\${TG_ROOT}"
- echo "CI_PROJECT_DIR=\${CI_PROJECT_DIR}"
- export AWS_ACCESS_KEY_ID="\${AWS_ACCESS_KEY_ID}"
- export AWS_SECRET_ACCESS_KEY="\${AWS_SECRET_ACCESS_KEY}"
- export S3_ACCESS_KEY="\${S3_ACCESS_KEY}"
- export S3_SECRET_KEY="\${S3_SECRET_KEY}"
- cd \${TG_ROOT} - cd \${TG_ROOT}
- echo "Working directory:" && pwd
- ls -la - ls -la
- export TF_PLUGIN_CACHE_DIR=/root/.terraform.d/plugin-cache
script: script:
- env | grep -E "(S3_|AWS_)" || echo "No S3/AWS vars found"
- rm -f .terraform.lock.hcl
- terragrunt init -upgrade -reconfigure - terragrunt init -upgrade -reconfigure
- terragrunt validate - terragrunt validate
rules: rules:
@ -110,46 +99,24 @@ validate-${job_prefix}:
interruptible: true interruptible: true
artifacts: artifacts:
paths: paths:
- "\${TG_ROOT}/.terragrunt-cache/**/*" - ${full_component_dir}/.terragrunt-cache/
expire_in: 1 hour expire_in: 1 hour
when: always when: always
EOF ${needs_section}
if [ -n "$needs_deps" ]; then VALIDATE_JOB
cat >> "$OUTPUT_FILE" << EOF
needs:
EOF
for dep in $needs_deps; do
echo " - ${dep}" >> "$OUTPUT_FILE"
done
cat >> "$OUTPUT_FILE" << EOF
dependencies:
EOF
for dep in $needs_deps; do
echo " - ${dep}" >> "$OUTPUT_FILE"
done
fi
cat >> "$OUTPUT_FILE" << EOF
cat >> "$OUTPUT_FILE" << PLAN_JOB
plan-${job_prefix}: plan-${job_prefix}:
stage: plan stage: plan
image: \${TERRAFORM_IMAGE_NAME} image: \${TERRAFORM_IMAGE_NAME}
variables: variables:
TG_ROOT: "${component_dir}" TG_ROOT: "${full_component_dir}"
ENVIRONMENT: "${env}" ENVIRONMENT: "${env}"
YC_FOLDER_ID: "\${${folder_var}}" YC_FOLDER_ID: "\${${folder_var}}"
YC_TOKEN: "\${YC_IAM_TOKEN}" needs:
S3_ACCESS_KEY: "\${S3_ACCESS_KEY}" - validate-${job_prefix}
S3_SECRET_KEY: "\${S3_SECRET_KEY}"
S3_STATE_BUCKET: "\${S3_STATE_BUCKET}"
TG_TF_PATH: "/usr/local/bin/terraform"
AWS_ACCESS_KEY_ID: "\${AWS_ACCESS_KEY_ID}"
AWS_SECRET_ACCESS_KEY: "\${AWS_SECRET_ACCESS_KEY}"
before_script: before_script:
- echo "${TG_ROOT}"
- export AWS_ACCESS_KEY_ID="\${AWS_ACCESS_KEY_ID}"
- export AWS_SECRET_ACCESS_KEY="\${AWS_SECRET_ACCESS_KEY}"
- cd \${TG_ROOT} - cd \${TG_ROOT}
- ls -la - ls -la
script: script:
@ -163,12 +130,12 @@ plan-${job_prefix}:
interruptible: true interruptible: true
artifacts: artifacts:
paths: paths:
- "\${TG_ROOT}/tfplan" - ${full_component_dir}/tfplan
- "\${TG_ROOT}/.terragrunt-cache/**/*" - ${full_component_dir}/.terragrunt-cache/
expire_in: 1 week expire_in: 1 week
when: always when: always
EOF PLAN_JOB
if [ "$env" = "prod" ]; then if [ "$env" = "prod" ]; then
when_clause="manual" when_clause="manual"
@ -176,30 +143,31 @@ EOF
when_clause="on_success" when_clause="on_success"
fi fi
cat >> "$OUTPUT_FILE" << EOF # Apply dependencies для secrets
apply_needs="plan-${job_prefix}"
if [ "$component" = "secrets" ]; then
apply_needs="plan-${job_prefix}
- apply-${env}-namespace
- apply-${env}-s3
- apply-${env}-database"
fi
cat >> "$OUTPUT_FILE" << APPLY_JOB
apply-${job_prefix}: apply-${job_prefix}:
stage: apply stage: apply
image: \${TERRAFORM_IMAGE_NAME} image: \${TERRAFORM_IMAGE_NAME}
variables: variables:
TG_ROOT: "${component_dir}" TG_ROOT: "${full_component_dir}"
ENVIRONMENT: "${env}" ENVIRONMENT: "${env}"
YC_FOLDER_ID: "\${${folder_var}}" YC_FOLDER_ID: "\${${folder_var}}"
YC_TOKEN: "\${YC_IAM_TOKEN}" needs:
S3_ACCESS_KEY: "\${S3_ACCESS_KEY}" - ${apply_needs}
S3_SECRET_KEY: "\${S3_SECRET_KEY}"
S3_STATE_BUCKET: "\${S3_STATE_BUCKET}"
TG_TF_PATH: "/usr/local/bin/terraform"
AWS_ACCESS_KEY_ID: "\${AWS_ACCESS_KEY_ID}"
AWS_SECRET_ACCESS_KEY: "\${AWS_SECRET_ACCESS_KEY}"
before_script: before_script:
- echo "${TG_ROOT}"
- export AWS_ACCESS_KEY_ID="\${AWS_ACCESS_KEY_ID}"
- export AWS_SECRET_ACCESS_KEY="\${AWS_SECRET_ACCESS_KEY}"
- cd \${TG_ROOT} - cd \${TG_ROOT}
- ls -la - ls -la
script: script:
- terragrunt init -upgrade -reconfigure - terragrunt init -upgrade -reconfigure
- terragrunt apply tfplan - terragrunt apply -auto-approve
rules: rules:
- if: '\$CI_COMMIT_BRANCH == "master" || \$CI_COMMIT_BRANCH == "main"' - if: '\$CI_COMMIT_BRANCH == "master" || \$CI_COMMIT_BRANCH == "main"'
when: ${when_clause} when: ${when_clause}
@ -208,13 +176,11 @@ apply-${job_prefix}:
- if: '\$CI_PIPELINE_SOURCE == "pipeline"' - if: '\$CI_PIPELINE_SOURCE == "pipeline"'
when: ${when_clause} when: ${when_clause}
interruptible: true interruptible: true
dependencies:
- plan-${job_prefix}
EOF APPLY_JOB
done done
echo "Pipeline generated successfully: $OUTPUT_FILE" echo "Pipeline generated successfully: $OUTPUT_FILE"
echo "Found components:" echo "Found components:"
find live -name "terragrunt.hcl" -not -path "*/.terragrunt-cache/*" | sed 's|live/||; s|/terragrunt.hcl||' | sort find live -name "terragrunt.hcl" -not -path "*/.terragrunt-cache/*" | sed 's|live/||; s|/terragrunt.hcl||' | grep '/' | sort