Skip to content

Commit 75e2cde

Browse files
malessimjburling
andauthored
BFD-4660: v3 Deployment Readiness (#3095)
Co-authored-by: Michael J Burling <michael.burling@cms.hhs.gov>
1 parent e15da05 commit 75e2cde

59 files changed

Lines changed: 2059 additions & 874 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/actions/bfd-deploy-env-service/action.yml

Lines changed: 0 additions & 122 deletions
This file was deleted.

.github/actions/bfd-deploy-platform-service/action.yml

Lines changed: 0 additions & 80 deletions
This file was deleted.
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
name: Deploy Tofuservice
2+
description: Composite action to deploy a BFD Tofuservice
3+
inputs:
4+
service-type:
5+
description: Type of Tofuservice. Either "PLATFORM" or "ENVIRONMENT"
6+
env-or-account:
7+
description: The environment (if env service) or account (if platform service)
8+
required: true
9+
service-path:
10+
description: "The path to the Tofuservice relative to the root of the repository"
11+
required: true
12+
cw-log-group:
13+
description: >-
14+
Name of CloudWatch Log Group to submit OpenTofu logs to; will be created if necessary
15+
required: true
16+
cw-log-stream:
17+
description: >-
18+
Name of CloudWatch Log Stream to submit OpenTofu logs to; will be created if necessary
19+
required: true
20+
skip-apply:
21+
description: >-
22+
Skip the apply of the given Tofuservice. Useful for logging the plan without making any
23+
modifications
24+
default: "false" # Composite Action inputs must be strings
25+
required: false
26+
tofu-vars-json:
27+
description: JSON object map of variables to their values
28+
required: false
29+
default: "{}"
30+
runs:
31+
using: composite
32+
steps:
33+
- name: Setup OpenTofu and shell environment
34+
id: setup-tofu
35+
uses: ./.github/actions/bfd-setup-tofu
36+
with:
37+
service-path: ${{ inputs.service-path }}
38+
cw-log-group: ${{ inputs.cw-log-group }}
39+
cw-log-stream: ${{ inputs.cw-log-stream }}
40+
tofu-vars-json: ${{ inputs.tofu-vars-json }}
41+
42+
# This step is necessary as it seems that some objects (like "github" or "inputs") are
43+
# unavailable when the top-level "env" is evaluated for Composite Actions
44+
- name: Setup environment
45+
run: |
46+
echo "TOFU_CI_SCRIPT=${{ github.workspace }}/.github/scripts/tofu-ci.sh" >> $GITHUB_ENV
47+
shell: bash
48+
49+
- name: Get parent environment
50+
if: ${{ inputs.service-type == 'ENVIRONMENT' }}
51+
id: get-parent-env
52+
env:
53+
ENV_OR_ACCOUNT: ${{ inputs.env-or-account }}
54+
run: |
55+
parent_env="$(echo "$ENV_OR_ACCOUNT" | grep -Po '(prod|sandbox|test)$')"
56+
57+
echo "parent-env=$parent_env" >> $GITHUB_OUTPUT
58+
shell: bash
59+
60+
- name: OpenTofu init
61+
env:
62+
SERVICE_TYPE: ${{ inputs.service-type }}
63+
PARENT_ENV: ${{ steps.get-parent-env.outputs.parent-env }}
64+
ENV_OR_ACCOUNT: ${{ inputs.env-or-account }}
65+
run: |
66+
tofu --version
67+
68+
# Often OpenTofu stdout/stderr logs contain sensitive/private information. GHA logs are
69+
# available for anyone logged in with a GitHub account to view, and so this information must
70+
# be protected. Instead of logging to stdout, all potentially sensitive OpenTofu log output
71+
# is instead logged to CloudWatch
72+
if [[ $SERVICE_TYPE == "PLATFORM" ]]; then
73+
TF_WORKSPACE=default tofu init \
74+
-var account_type="$ENV_OR_ACCOUNT" \
75+
-reconfigure \
76+
-no-color 2>&1 | "$STDOUT_TO_CWLOGS_SCRIPT"
77+
else
78+
TF_WORKSPACE=default tofu init \
79+
-var parent_env="$PARENT_ENV" \
80+
-reconfigure \
81+
-no-color 2>&1 | "$STDOUT_TO_CWLOGS_SCRIPT"
82+
fi
83+
echo "tofu init for \"$ENV_OR_ACCOUNT\" completed"
84+
85+
if [[ $SERVICE_TYPE == "PLATFORM" ]]; then
86+
tofu workspace select \
87+
-no-color \
88+
-var account_type="$ENV_OR_ACCOUNT" \
89+
-or-create "$ENV_OR_ACCOUNT" 2>&1 | "$STDOUT_TO_CWLOGS_SCRIPT"
90+
else
91+
tofu workspace select \
92+
-no-color \
93+
-var parent_env="$PARENT_ENV" \
94+
-or-create "$ENV_OR_ACCOUNT" 2>&1 | "$STDOUT_TO_CWLOGS_SCRIPT"
95+
fi
96+
echo "Selected workspace \"$ENV_OR_ACCOUNT\""
97+
shell: bash
98+
working-directory: ${{ github.workspace }}/${{ inputs.service-path }}
99+
100+
# We split Tofu steps into "start"/"do"/"end" steps in order to attempt to avoid GitHub
101+
# Actions' poor signaling behavior on exit.
102+
# See the README of https://github.com/ringerc/github-actions-signal-handling-demo and
103+
# https://github.com/orgs/community/discussions/26311
104+
- name: Start OpenTofu Plan
105+
env:
106+
SERVICE_PATH: ${{ inputs.service-path }}
107+
run: |
108+
echo "Generating OpenTofu plan for $SERVICE_PATH..."
109+
shell: bash
110+
working-directory: ${{ github.workspace }}/${{ inputs.service-path }}
111+
112+
# Having this "exec ..." be the first command should ensure that the wrapper script is the
113+
# "leader" process
114+
- name: Do OpenTofu plan
115+
run: |
116+
exec "$TOFU_CI_SCRIPT" plan ${{ steps.setup-tofu.outputs.tf-vars-args }} -no-color \
117+
-out=tfplan 2>&1 | "$STDOUT_TO_CWLOGS_SCRIPT"
118+
shell: bash
119+
working-directory: ${{ github.workspace }}/${{ inputs.service-path }}
120+
121+
- name: End OpenTofu plan
122+
env:
123+
SERVICE_PATH: ${{ inputs.service-path }}
124+
run: |
125+
echo "Plan generated for $SERVICE_PATH successfully"
126+
shell: bash
127+
128+
- name: Start apply env service
129+
if: ${{ inputs.skip-apply != 'true' }}
130+
env:
131+
SERVICE_PATH: ${{ inputs.service-path }}
132+
run: |
133+
echo "Applying OpenTofu plan for $SERVICE_PATH..."
134+
shell: bash
135+
working-directory: ${{ github.workspace }}/${{ inputs.service-path }}
136+
137+
- name: Do apply env service
138+
if: ${{ inputs.skip-apply != 'true' }}
139+
run: |
140+
exec "$TOFU_CI_SCRIPT" apply -no-color -input=false tfplan 2>&1 | "$STDOUT_TO_CWLOGS_SCRIPT"
141+
shell: bash
142+
working-directory: ${{ github.workspace }}/${{ inputs.service-path }}
143+
144+
- name: End apply env service
145+
if: ${{ inputs.skip-apply != 'true' }}
146+
env:
147+
SERVICE_PATH: ${{ inputs.service-path }}
148+
run: |
149+
echo "OpenTofu plan for $SERVICE_PATH applied"
150+
shell: bash
151+
152+
- name: Force-unlock tofu state (on failure/cancel)
153+
if: failure() || cancelled()
154+
continue-on-error: true
155+
shell: bash
156+
run: |
157+
set -euo pipefail
158+
159+
run_with_auto_unlock() {
160+
# Run the command, capture output
161+
set +e
162+
out="$("$@" 2>&1)"
163+
rc=$?
164+
set -e
165+
166+
if [[ $rc -eq 0 ]]; then
167+
return 0
168+
fi
169+
170+
# Detect lock failure + extract ID
171+
if echo "$out" | grep -q "Error acquiring the state lock" && echo "$out" | grep -q "Lock Info:"; then
172+
lock_id="$(echo "$out" | grep ' ID: ' | cut -d: -f2 | tr -d ' ' || true)"
173+
if [[ -n "${lock_id:-}" ]]; then
174+
echo "Detected lock; force-unlocking: $lock_id"
175+
tofu force-unlock -force "$lock_id" -no-color
176+
return 0
177+
fi
178+
fi
179+
180+
return $rc
181+
}
182+
183+
run_with_auto_unlock tofu plan -no-color -refresh=false -lock-timeout=10s
184+
working-directory: ${{ github.workspace }}/${{ inputs.service-path }}

0 commit comments

Comments
 (0)