Skip to content

Commit

Permalink
feat: added apply strategies
Browse files Browse the repository at this point in the history
  • Loading branch information
Alina Freydina committed Dec 8, 2023
1 parent 777a0fc commit ef1ec95
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 67 deletions.
15 changes: 7 additions & 8 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
# this file is for the labeler workflow job
# Documentation https://github.com/marketplace/actions/labeler

'type: documentation':
- assets/**/*
- .github/*
- ./*.md

'type: maintenance':
- .github/**/*
- tests/**/*
"type: docs":
- changed-files:
- any-glob-to-any-file: [assets/**/*, .github/*, ./*.md]

"type: maintenance":
- changed-files:
- any-glob-to-any-file: .github/**/*

...
4 changes: 2 additions & 2 deletions .github/workflows/deploy-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ name: 📌 Deploy release

on:
release:
types:
- released
types: [published]
workflow_dispatch:
inputs:
tag_name:
Expand All @@ -20,6 +19,7 @@ jobs:
image:
runs-on: ubuntu-latest
name: Release Actions

steps:
- name: 📦 Checkout
uses: actions/checkout@v3
Expand Down
2 changes: 1 addition & 1 deletion example_workflows/create_plan.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
uses: wayofdev/gh-action-terragrunt-plan@v1
with:
path: ingrastructure
tg_version: '0.52.4'
tg_version: 'v0.52.4'
tf_version: '1.5.7'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion example_workflows/create_plan_for_destroy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
uses: wayofdev/gh-action-terragrunt-plan@v1
with:
path: ingrastructure
tg_version: '0.52.4'
tg_version: 'v0.52.4'
tf_version: '1.5.7'
destroy: true
env:
Expand Down
24 changes: 18 additions & 6 deletions gh-action-terragrunt-apply/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@

This Terragrunt action based on [dflook/terraform-github-actions](https://github.com/dflook/terraform-github-actions).

This action applies a Terraform plan for each module in the provided path. The default behaviour is to apply the plan that has been added to a PR using the wayofdev/gh-action-terragrunt-plan action.
This action applies a Terraform plan for each module in the provided path. The default behaviour is to apply the plan that has been added to a PR using the Fenikks/terragrunt-plan-all action.

If the plan is not found or has changed, then the apply action will fail. This is to ensure that the action only applies changes that have been reviewed by a human.

You can instead set `auto_approve: true` which will generate a plan and apply it immediately, without looking for a plan attached to a PR.

**NOTE:** This github action uses default terragrunt cache folder `.terragrunt-cache` to create plan and then to read it.
Don't use terragrunt_download setting in your terragrunt code and also don't clear cache. Otherwise the action won't work.
>**NOTE:**
>There are two apply strategies. Read about them bellow in Inputs section.
This github action uses --terragrunt-download-dir option to redirect cache in `/tmp/tg_cache_dir`.

## Inputs

These input values must be the same as any wayofdev/gh-action-terragrunt-plan for the same configuration. (unless auto_approve: true)
These input values must be the same as any Fenikks/terragrunt-plan-all for the same configuration, except strategy because it is actual only for apply command. (unless auto_approve: true)

* `path`

Expand Down Expand Up @@ -73,10 +75,20 @@ These input values must be the same as any wayofdev/gh-action-terragrunt-plan fo

The default is false, which requires plans to have been approved through a pull request.

- Type: bool
- Type: boolean
- Optional
- Default: false

* `strategy`

When set to **parallel**(default) `terragrunt run-all apply` will be executed in provided `path`. And terragrunt will execute multiple plans in parallel accordint to parallelism settings. Plan will be applied even if there is no changes for particular module.

When set to **sequential** terragrunt will change into each module directory individually and execute `terragrunt run-all apply` and only when there are changes in the plan. So it will skip modules without changes and apply changes one by one.

- Type: string
- Optional
- Default: parallel

## Environment Variables

* `GITHUB_TOKEN`
Expand Down Expand Up @@ -168,7 +180,7 @@ A minimal example payload looks like:
```json
{
"pull_request": {
"url": "https://api.github.com/repos/wayofdev/gh-actions-terragrunt/pulls/1"
"url": "https://api.github.com/repos/Fenikks/gh-actions-terragrunt/pulls/1"
}
}
```
Expand Down
4 changes: 4 additions & 0 deletions gh-action-terragrunt-apply/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ inputs:
description: Automatically approve and apply plans
required: false
default: "false"
strategy:
description: "What strategy to use when applying: parallel or sequential"
required: false
default: "parallel"

runs:
using: docker
Expand Down
83 changes: 63 additions & 20 deletions image/actions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function setup() {
TF_VERSION=$INPUT_TF_VERSION
fi

curl -Lo /usr/local/bin/terragrunt "https://github.com/gruntwork-io/terragrunt/releases/download/v${TG_VERSION}/terragrunt_linux_amd64"
curl -Lo /usr/local/bin/terragrunt "https://github.com/gruntwork-io/terragrunt/releases/download/${TG_VERSION}/terragrunt_linux_amd64"
chmod +x /usr/local/bin/terragrunt
curl -o /tmp/terraform_${TF_VERSION}_linux_amd64.zip https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip
unzip /tmp/terraform_${TF_VERSION}_linux_amd64.zip -d /usr/local/bin/
Expand Down Expand Up @@ -80,42 +80,82 @@ function set_common_plan_args() {
function plan() {

# shellcheck disable=SC2086
debug_log terragrunt run-all plan -input=false -no-color -detailed-exitcode -lock-timeout=300s $PARALLEL_ARG -out=plan.out '$PLAN_ARGS' # don't expand PLAN_ARGS
debug_log terragrunt run-all plan --terragrunt-download-dir $TG_CACHE_DIR -input=false -no-color -detailed-exitcode -lock-timeout=300s $PARALLEL_ARG -out=plan.out '$PLAN_ARGS' # don't expand PLAN_ARGS

MODULE_PATHS=$(find $INPUT_PATH -mindepth 2 -name terragrunt.hcl -exec dirname {} \;)
# Get a list of all modules in the provided path
MODULE_PATHS=$(terragrunt output-module-groups --terragrunt-working-dir $INPUT_PATH|jq -r 'to_entries | .[].value[]')
export MODULE_PATHS

start_group "List of modules found in the provided input path"
for p in $MODULE_PATHS; do
echo "- ${INPUT_PATH}${p#*${INPUT_PATH#./}}"
done
end_group

set +e
# shellcheck disable=SC2086
start_group "Generating plan"
(cd "$INPUT_PATH" && terragrunt run-all plan -input=false -no-color -detailed-exitcode -lock-timeout=300s $PARALLEL_ARG -out=plan.out $PLAN_ARGS) \
2>"$STEP_TMP_DIR/terraform_plan.stderr" \
| $TFMASK
(
(cd "$INPUT_PATH" && terragrunt run-all plan --terragrunt-download-dir $TG_CACHE_DIR -input=false -no-color -detailed-exitcode -lock-timeout=300s $PARALLEL_ARG -out=plan.out $PLAN_ARGS) \
2>"$STEP_TMP_DIR/terraform_plan.stderr" \
| $TFMASK
wait
)
end_group

# Generate text file for each plan
start_group "Generating plan it text format"
# shellcheck disable=SC2034
for i in $MODULE_PATHS; do
plan_name=${i//.\//}
plan_name=${plan_name//\//___}
terragrunt show plan.out --terragrunt-working-dir $i -no-color 2>"$STEP_TMP_DIR/terraform_show_plan.stderr" \
for i in $MODULE_PATHS; do
plan_name=${i//\//___}
terragrunt show plan.out --terragrunt-working-dir $i -no-color --terragrunt-download-dir $TG_CACHE_DIR 2>"$STEP_TMP_DIR/terraform_show_plan.stderr" \
|tee $PLAN_OUT_DIR/$plan_name
done
end_group
set -e
}

function apply() {


# shellcheck disable=SC2086
debug_log terragrunt run-all apply --terragrunt-download-dir $TG_CACHE_DIR -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG '$PLAN_ARGS' plan.out

set +e
start_group "Applying plan sequentially"
(
for i in $MODULE_PATHS; do
plan_name=${i//\//___}
if grep -q "No changes." $PLAN_OUT_DIR/$plan_name; then
echo "There is no changes in the module ${INPUT_PATH}${i#*${INPUT_PATH#./}}, skiping plan apply for it"
continue
else
(cd $i && terragrunt run-all apply --terragrunt-download-dir $TG_CACHE_DIR -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG $PLAN_ARGS plan.out) \
2>"$STEP_TMP_DIR/terraform_apply_error/${plan_name}.stderr" \
| $TFMASK \
| tee /dev/fd/3 "$STEP_TMP_DIR/terraform_apply_stdout/${plan_name}.stdout"
fi
done
wait
)
end_group
set -e
}

function apply_all() {

# shellcheck disable=SC2086
debug_log terragrunt run-all apply -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG '$PLAN_ARGS'
debug_log terragrunt run-all apply --terragrunt-download-dir $TG_CACHE_DIR -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG '$PLAN_ARGS' plan.out

set +e
start_group "Applying plan"
start_group "Applying plan parallel"
# shellcheck disable=SC2086
(cd "$INPUT_PATH" && terragrunt run-all apply -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG $PLAN_ARGS) \
2>"$STEP_TMP_DIR/terraform_apply.stderr" \
| $TFMASK \
| tee /dev/fd/3 "$STEP_TMP_DIR/terraform_apply.stdout"
(
(cd "$INPUT_PATH" && terragrunt run-all apply --terragrunt-download-dir $TG_CACHE_DIR -input=false -no-color -auto-approve -lock-timeout=300s $PARALLEL_ARG $PLAN_ARGS plan.out) \
2>"$STEP_TMP_DIR/terraform_apply.stderr" \
| $TFMASK \
| tee /dev/fd/3 "$STEP_TMP_DIR/terraform_apply.stdout"
wait
)
end_group
set -e
}
Expand Down Expand Up @@ -177,10 +217,13 @@ function fix_owners() {
# Every file written to disk should use one of these directories
STEP_TMP_DIR="/tmp"
PLAN_OUT_DIR="/tmp/plan"
TG_CACHE_DIR="/tmp/tg_cache_dir"
JOB_TMP_DIR="$HOME/.gh-actions-terragrunt"
WORKSPACE_TMP_DIR=".gh-actions-terragrunt/$(random_string)"
mkdir -p $PLAN_OUT_DIR
readonly STEP_TMP_DIR JOB_TMP_DIR WORKSPACE_TMP_DIR PLAN_OUT_DIR
export STEP_TMP_DIR JOB_TMP_DIR WORKSPACE_TMP_DIR PLAN_OUT_DIR
mkdir -p $PLAN_OUT_DIR $TG_CACHE_DIR
mkdir -p $STEP_TMP_DIR/terraform_apply_stdout
mkdir -p $STEP_TMP_DIR/terraform_apply_error
readonly STEP_TMP_DIR JOB_TMP_DIR WORKSPACE_TMP_DIR PLAN_OUT_DIR TG_CACHE_DIR
export STEP_TMP_DIR JOB_TMP_DIR WORKSPACE_TMP_DIR PLAN_OUT_DIR TG_CACHE_DIR

trap fix_owners EXIT
90 changes: 73 additions & 17 deletions image/entrypoints/tg_apply_all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,19 @@ end_group

# Check if state is locked
if lock-info "$STEP_TMP_DIR/terraform_plan.stderr"; then
update_status ":x: Error applying plan in $(job_markdown_ref)(State is locked)"
update_status ":x: Error applying plan in $(job_markdown_ref) (State is locked)"
exit 1
fi

### Apply the plan

# Apply the plan
if [[ "$INPUT_AUTO_APPROVE" == "true" ]]; then
echo "Automatically approving plan"
apply
if [[ "$INPUT_STRATEGY" == "parallel" ]]; then
apply_all
else
apply
fi

else
if [[ "$GITHUB_EVENT_NAME" != "push" && "$GITHUB_EVENT_NAME" != "pull_request" && "$GITHUB_EVENT_NAME" != "issue_comment" && "$GITHUB_EVENT_NAME" != "pull_request_review_comment" && "$GITHUB_EVENT_NAME" != "pull_request_target" && "$GITHUB_EVENT_NAME" != "pull_request_review" && "$GITHUB_EVENT_NAME" != "repository_dispatch" ]]; then
Expand All @@ -50,29 +54,81 @@ else
fi

if github_pr_comment approved; then
apply
if [[ "$INPUT_STRATEGY" == "parallel" ]]; then
apply_all
else
apply
fi
else
exit 1
fi
fi

start_group "Content of terraform_apply.stderr"
cat "$STEP_TMP_DIR/terraform_apply.stderr"
end_group

start_group "Content of terraform_apply.stdout"
cat "$STEP_TMP_DIR/terraform_apply.stdout"
end_group
if [[ "$INPUT_STRATEGY" == "parallel" ]]; then
start_group "Content of terraform_apply.stderr"
cat >&2 "$STEP_TMP_DIR/terraform_apply.stderr"
end_group

start_group "Content of terraform_apply.stdout"
cat >&2 "$STEP_TMP_DIR/terraform_apply.stdout"
end_group

# check if there are errors in terraform_apply.stderr
if lock-info "$STEP_TMP_DIR/terraform_apply.stderr"; then
update_status ":x: Error applying plan in $(job_markdown_ref) (State is locked)"
exit 1
else
for code in $(tac $STEP_TMP_DIR/terraform_apply.stderr | awk '/^[[:space:]]*\*/{flag=1; print} flag && /^[[:space:]]*time=/{exit}' | awk '{print $5}'); do
if [[ $code -eq 1 ]]; then
update_status ":x: Error applying plan in $(job_markdown_ref)"
exit 1
fi
done
fi

# check if there are errors in terraform_apply.stderr
if lock-info "$STEP_TMP_DIR/terraform_apply.stderr"; then
update_status ":x: Error applying plan in $(job_markdown_ref)(State is locked)"
exit 1
else
for code in $(tac $STEP_TMP_DIR/terraform_apply.stderr | awk '/^[[:space:]]*\*/{flag=1; print} flag && /^[[:space:]]*time=/{exit}' | awk '{print $5}'); do
if [[ $code -eq 1 ]]; then
update_status ":x: Error applying plan in $(job_markdown_ref)"
# If there is no files in terraform_apply_error and in terraform_apply_stdout, then there is no changes in the plan
if [[ ! "$(ls $STEP_TMP_DIR/terraform_apply_error/*.stderr 2>/dev/null)" ]] && [[ ! "$(ls $STEP_TMP_DIR/terraform_apply_error/*.stdout 2>/dev/null)" ]]; then
echo "No changes in the plan, skipping apply"
update_status ":white_check_mark: No changes to apply in $(job_markdown_ref)"
exit 0
fi

echo "Apply errors by module:"
for file in $STEP_TMP_DIR/terraform_apply_error/*; do
if [[ -s $file ]]; then
filename=$(basename "$file")
filename=${filename//___/\/}
start_group "${INPUT_PATH}${filename#*${INPUT_PATH#./}}"
cat $file
end_group
fi
done

echo "Apply output by module:"
for file in $STEP_TMP_DIR/terraform_apply_stdout/*; do
if [[ -s $file ]]; then
filename=$(basename "$file")
filename=${filename//___/\/}
start_group "${INPUT_PATH}${filename#*${INPUT_PATH#./}}"
cat $file
end_group
fi
done

# check if there are errors in terraform_apply.stderr
for file in $STEP_TMP_DIR/terraform_apply_error/*; do
if lock-info "$file"; then
update_status ":x: Error applying plan in $(job_markdown_ref) (State is locked)"
exit 1
else
for code in $(tac $file | awk '/^[[:space:]]*\*/{flag=1; print} flag && /^[[:space:]]*time=/{exit}' | awk '{print $5}'); do
if [[ $code -eq 1 ]]; then
update_status ":x: Error applying plan in $(job_markdown_ref)"
exit 1
fi
done
fi
done
fi
Expand Down
Loading

0 comments on commit ef1ec95

Please sign in to comment.