mirror of
https://github.com/coder/coder.git
synced 2026-11-09 03:43:54 +08:00
The terraform testdata fixtures silently drift when the coder provider releases a new version. The .terraform.lock.hcl files are gitignored, .tf files use loose constraints (>= 2.0.0), and generate.sh always runs terraform init -upgrade. The Makefile only re-runs generate.sh when the terraform CLI version changes, not the provider version. Track a canonical lockfile and provider-version.txt in git. Change generate.sh to respect the lockfile by default (terraform init without -upgrade). Add --upgrade flag for intentional provider bumps, --check for cheap staleness detection in the Makefile, and a new update-terraform-testdata make target.
205 lines
5.0 KiB
Bash
Executable File
205 lines
5.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
# Resolve paths before cd so they're absolute.
|
|
scriptdir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
cd "$scriptdir/resources"
|
|
canonical_lock="$(pwd)/.terraform.lock.hcl"
|
|
|
|
# These environment variables influence the coder provider.
|
|
for v in $(env | grep -E '^CODER_' | cut -d= -f1); do
|
|
unset "$v"
|
|
done
|
|
|
|
generate() {
|
|
local name="$1"
|
|
|
|
echo "=== BEGIN: $name"
|
|
if ((upgrade)); then
|
|
terraform init -upgrade
|
|
else
|
|
terraform init
|
|
fi &&
|
|
terraform plan -out terraform.tfplan &&
|
|
terraform show -json ./terraform.tfplan | jq >"$name".tfplan.json &&
|
|
terraform graph -type=plan >"$name".tfplan.dot &&
|
|
rm terraform.tfplan &&
|
|
terraform apply -auto-approve &&
|
|
terraform show -json ./terraform.tfstate | jq >"$name".tfstate.json &&
|
|
rm terraform.tfstate &&
|
|
terraform graph -type=plan >"$name".tfstate.dot
|
|
ret=$?
|
|
echo "=== END: $name"
|
|
if [[ $ret -ne 0 ]]; then
|
|
return $ret
|
|
fi
|
|
}
|
|
|
|
minimize_diff() {
|
|
for f in *.tf*.json; do
|
|
declare -A deleted=()
|
|
declare -a sed_args=()
|
|
while read -r line; do
|
|
# Deleted line (previous value).
|
|
if [[ $line = -\ * ]]; then
|
|
key="${line#*\"}"
|
|
key="${key%%\"*}"
|
|
value="${line#*: }"
|
|
value="${value#*\"}"
|
|
value="\"${value%\"*}\""
|
|
declare deleted["$key"]="$value"
|
|
# Added line (new value).
|
|
elif [[ $line = +\ * ]]; then
|
|
key="${line#*\"}"
|
|
key="${key%%\"*}"
|
|
value="${line#*: }"
|
|
value="${value#*\"}"
|
|
value="\"${value%\"*}\""
|
|
# Matched key, restore the value.
|
|
if [[ -v deleted["$key"] ]]; then
|
|
sed_args+=(-e "s|${value}|${deleted["$key"]}|")
|
|
unset "deleted[$key]"
|
|
fi
|
|
fi
|
|
if [[ ${#sed_args[@]} -gt 0 ]]; then
|
|
# Handle macOS compat.
|
|
if grep -q -- "\[-i extension\]" < <(sed -h 2>&1); then
|
|
sed -i '' "${sed_args[@]}" "$f"
|
|
else
|
|
sed -i'' "${sed_args[@]}" "$f"
|
|
fi
|
|
fi
|
|
done < <(
|
|
# Filter out known keys with autogenerated values.
|
|
git diff -- "$f" |
|
|
grep -E "\"(terraform_version|id|agent_id|subagent_id|resource_id|token|random|timestamp)\":"
|
|
)
|
|
done
|
|
}
|
|
|
|
run() {
|
|
d="$1"
|
|
cd "$d"
|
|
name=$(basename "$(pwd)")
|
|
|
|
toskip=(
|
|
# This needs care to update correctly.
|
|
"kubernetes-metadata"
|
|
# Multiple resources with duplicate JSON key names (id, agent_id)
|
|
# cause minimize_diff() to scramble UUIDs. Hand-crafted fixture.
|
|
"duplicate-env-keys"
|
|
)
|
|
for skip in "${toskip[@]}"; do
|
|
if [[ $name == "$skip" ]]; then
|
|
echo "== Skipping: $name"
|
|
touch "$name.tfplan.json" "$name.tfplan.dot" "$name.tfstate.json" "$name.tfstate.dot"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
echo "== Generating test data for: $name"
|
|
if ! out="$(generate "$name" 2>&1)"; then
|
|
echo "$out"
|
|
echo "== Error generating test data for: $name"
|
|
return 1
|
|
fi
|
|
if ((minimize)); then
|
|
echo "== Minimizing diffs for: $name"
|
|
minimize_diff
|
|
fi
|
|
echo "== Done generating test data for: $name"
|
|
exit 0
|
|
}
|
|
|
|
if [[ " $* " == *" --help "* || " $* " == *" -h "* ]]; then
|
|
echo "Usage: $0 [--upgrade] [--check] [--no-minimize] [module1 module2 ...]"
|
|
exit 0
|
|
fi
|
|
|
|
minimize=1
|
|
if [[ " $* " == *" --no-minimize "* ]]; then
|
|
minimize=0
|
|
fi
|
|
|
|
upgrade=0
|
|
if [[ " $* " == *" --upgrade "* ]]; then
|
|
upgrade=1
|
|
fi
|
|
|
|
# Verify that the canonical lockfile matches provider-version.txt.
|
|
if [[ " $* " == *" --check "* ]]; then
|
|
expected="$(<"$scriptdir/provider-version.txt")"
|
|
actual="$(sed -n '/coder\/coder/,/^}/{ /version[[:space:]]*=/{ s/.*"\(.*\)"/\1/; p; q; } }' "$canonical_lock")"
|
|
if [[ "$expected" == "$actual" ]]; then
|
|
exit 0
|
|
else
|
|
echo "ERROR: provider-version.txt ($expected) does not match lockfile ($actual)"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Filter flags from positional args to get directory names.
|
|
declare -a dirs=()
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--upgrade | --no-minimize | --check | --help | -h) ;;
|
|
*) dirs+=("$arg") ;;
|
|
esac
|
|
done
|
|
|
|
# Seed each resource subdirectory with the canonical lockfile.
|
|
for d in */; do
|
|
cp "$canonical_lock" "$d/.terraform.lock.hcl"
|
|
done
|
|
|
|
declare -a jobs=()
|
|
if [[ ${#dirs[@]} -gt 0 ]]; then
|
|
for d in "${dirs[@]}"; do
|
|
run "$d" &
|
|
jobs+=($!)
|
|
done
|
|
else
|
|
for d in */; do
|
|
run "$d" &
|
|
jobs+=($!)
|
|
done
|
|
fi
|
|
|
|
err=0
|
|
for job in "${jobs[@]}"; do
|
|
if ! wait "$job"; then
|
|
err=$((err + 1))
|
|
fi
|
|
done
|
|
if [[ $err -ne 0 ]]; then
|
|
echo "ERROR: Failed to generate test data for $err modules"
|
|
exit 1
|
|
fi
|
|
|
|
# After upgrade, promote the lockfile from a representative directory
|
|
# back to the canonical location and record the provider version.
|
|
if ((upgrade)); then
|
|
# Prefer rich-parameters since it uses all providers (coder, null, docker).
|
|
src=""
|
|
if [[ -f "rich-parameters/.terraform.lock.hcl" ]]; then
|
|
src="rich-parameters/.terraform.lock.hcl"
|
|
else
|
|
for d in */; do
|
|
if [[ -f "$d/.terraform.lock.hcl" ]]; then
|
|
src="$d/.terraform.lock.hcl"
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
if [[ -n "$src" ]]; then
|
|
cp "$src" "$canonical_lock"
|
|
version="$(sed -n '/coder\/coder/,/^}/{ /version[[:space:]]*=/{ s/.*"\(.*\)"/\1/; p; q; } }' "$canonical_lock")"
|
|
echo "$version" >"$scriptdir/provider-version.txt"
|
|
echo "== Updated canonical lockfile and provider-version.txt (coder provider $version)"
|
|
fi
|
|
fi
|
|
|
|
terraform version -json | jq -r '.terraform_version' >../version.txt
|