forked from openemr/openemr-on-eks
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcontract-tests.bats
More file actions
301 lines (261 loc) · 12.3 KB
/
Copy pathcontract-tests.bats
File metadata and controls
301 lines (261 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
#!/usr/bin/env bats
# -----------------------------------------------------------------------------
# BATS Suite: Cross-file contract & consistency tests
# Purpose: Validate that versions, names, outputs, and references stay in sync
# across Terraform, K8s manifests, shell scripts, Dockerfiles, and
# versions.yaml. Catches the kind of drift that only surfaces at
# deploy time today.
# Scope: Read-only — inspects files, never modifies anything.
# -----------------------------------------------------------------------------
load test_helper
setup() {
VERSIONS_FILE="${PROJECT_ROOT}/versions.yaml"
OUTPUTS_TF="${PROJECT_ROOT}/terraform/outputs.tf"
CRED_ROT_TF="${PROJECT_ROOT}/terraform/credential-rotation.tf"
CRED_ROT_DOCKERFILE="${PROJECT_ROOT}/tools/credential-rotation/Dockerfile"
CRED_ROT_REQUIREMENTS="${PROJECT_ROOT}/tools/credential-rotation/requirements.txt"
WARP_REQUIREMENTS="${PROJECT_ROOT}/warp/requirements.txt"
CI_WORKFLOW="${PROJECT_ROOT}/.github/workflows/ci-cd-tests.yml"
K8S_DIR="${PROJECT_ROOT}/k8s"
}
# ── Helper: extract Terraform output names from outputs.tf + credential-rotation.tf ──
_all_tf_output_names() {
grep -h '^output "' "$OUTPUTS_TF" "$CRED_ROT_TF" 2>/dev/null \
| sed 's/output "\([^"]*\)".*/\1/' | sort -u
}
# ===========================================================================
# VERSION CONSISTENCY
# ===========================================================================
@test "CONTRACT: credential rotation Dockerfile PYTHON_VERSION matches versions.yaml" {
if ! command -v yq >/dev/null 2>&1; then skip "yq not installed"; fi
local yaml_ver
yaml_ver=$(yq eval '.applications.python.current' "$VERSIONS_FILE")
local docker_ver
docker_ver=$(grep '^ARG PYTHON_VERSION=' "$CRED_ROT_DOCKERFILE" | sed 's/ARG PYTHON_VERSION=//')
[ "$docker_ver" = "$yaml_ver" ]
}
@test "CONTRACT: credential rotation requirements.txt boto3 version matches versions.yaml" {
if ! command -v yq >/dev/null 2>&1; then skip "yq not installed"; fi
local yaml_ver
yaml_ver=$(yq eval '.python_packages.boto3.current' "$VERSIONS_FILE")
run grep '^boto3' "$CRED_ROT_REQUIREMENTS"
[[ "$output" == *"$yaml_ver"* ]]
}
@test "CONTRACT: credential rotation requirements.txt pymysql version matches versions.yaml" {
if ! command -v yq >/dev/null 2>&1; then skip "yq not installed"; fi
local yaml_ver
yaml_ver=$(yq eval '.python_packages.pymysql.current' "$VERSIONS_FILE")
run grep '^pymysql' "$CRED_ROT_REQUIREMENTS"
[[ "$output" == *"$yaml_ver"* ]]
}
@test "CONTRACT: credential rotation requirements.txt kubernetes version matches versions.yaml" {
if ! command -v yq >/dev/null 2>&1; then skip "yq not installed"; fi
local yaml_ver
yaml_ver=$(yq eval '.python_packages.kubernetes.current' "$VERSIONS_FILE")
run grep '^kubernetes' "$CRED_ROT_REQUIREMENTS"
[[ "$output" == *"$yaml_ver"* ]]
}
@test "CONTRACT: credential rotation requirements.txt requests version matches versions.yaml" {
if ! command -v yq >/dev/null 2>&1; then skip "yq not installed"; fi
local yaml_ver
yaml_ver=$(yq eval '.python_packages.requests.current' "$VERSIONS_FILE")
run grep '^requests' "$CRED_ROT_REQUIREMENTS"
[[ "$output" == *"$yaml_ver"* ]]
}
@test "CONTRACT: warp requirements.txt pymysql version matches versions.yaml" {
if ! command -v yq >/dev/null 2>&1; then skip "yq not installed"; fi
local yaml_ver
yaml_ver=$(yq eval '.python_packages.pymysql.current' "$VERSIONS_FILE")
run grep '^pymysql' "$WARP_REQUIREMENTS"
[[ "$output" == *"$yaml_ver"* ]]
}
@test "CONTRACT: CI workflow PYTHON_VERSION matches versions.yaml semver_packages" {
if ! command -v yq >/dev/null 2>&1; then skip "yq not installed"; fi
local yaml_ver
yaml_ver=$(yq eval '.semver_packages.python_version.current' "$VERSIONS_FILE")
run grep "PYTHON_VERSION:" "$CI_WORKFLOW"
[[ "$output" == *"$yaml_ver"* ]]
}
@test "CONTRACT: manual-releases workflow PYTHON_VERSION matches versions.yaml semver_packages" {
if ! command -v yq >/dev/null 2>&1; then skip "yq not installed"; fi
local yaml_ver
yaml_ver=$(yq eval '.semver_packages.python_version.current' "$VERSIONS_FILE")
run grep "PYTHON_VERSION:" "${PROJECT_ROOT}/.github/workflows/manual-releases.yml"
[[ "$output" == *"$yaml_ver"* ]]
}
@test "CONTRACT: warp CI workflow PYTHON_VERSION matches versions.yaml semver_packages" {
if ! command -v yq >/dev/null 2>&1; then skip "yq not installed"; fi
local yaml_ver
yaml_ver=$(yq eval '.semver_packages.python_version.current' "$VERSIONS_FILE")
run grep "PYTHON_VERSION:" "${PROJECT_ROOT}/warp/.github/workflows/ci.yml"
[[ "$output" == *"$yaml_ver"* ]]
}
@test "CONTRACT: CI workflow TERRAFORM_VERSION matches versions.yaml" {
if ! command -v yq >/dev/null 2>&1; then skip "yq not installed"; fi
local yaml_ver
yaml_ver=$(yq eval '.semver_packages.terraform_version.current' "$VERSIONS_FILE")
run grep "TERRAFORM_VERSION:" "$CI_WORKFLOW"
[[ "$output" == *"$yaml_ver"* ]]
}
@test "CONTRACT: CI workflow KUBECTL_VERSION matches versions.yaml" {
if ! command -v yq >/dev/null 2>&1; then skip "yq not installed"; fi
local yaml_ver
yaml_ver=$(yq eval '.semver_packages.kubectl_version.current' "$VERSIONS_FILE")
run grep "KUBECTL_VERSION:" "$CI_WORKFLOW"
[[ "$output" == *"$yaml_ver"* ]]
}
# ===========================================================================
# TERRAFORM-TO-SCRIPT CONTRACT
# ===========================================================================
@test "CONTRACT: run-credential-rotation.sh terraform outputs exist in .tf files" {
local script="${SCRIPTS_DIR}/run-credential-rotation.sh"
local outputs
outputs=$(_all_tf_output_names)
for name in rds_slot_secret_arn rds_admin_secret_arn credential_rotation_role_arn; do
echo "$outputs" | grep -qx "$name" || {
echo "Missing Terraform output: $name (referenced in run-credential-rotation.sh)"
return 1
}
done
}
@test "CONTRACT: verify-credential-rotation.sh terraform outputs exist in .tf files" {
local script="${SCRIPTS_DIR}/verify-credential-rotation.sh"
local outputs
outputs=$(_all_tf_output_names)
for name in rds_slot_secret_arn rds_admin_secret_arn credential_rotation_role_arn; do
echo "$outputs" | grep -qx "$name" || {
echo "Missing Terraform output: $name (referenced in verify-credential-rotation.sh)"
return 1
}
done
}
@test "CONTRACT: backup.sh terraform outputs exist in outputs.tf" {
local outputs
outputs=$(_all_tf_output_names)
echo "$outputs" | grep -qx "aurora_cluster_id"
}
@test "CONTRACT: restore.sh critical terraform outputs exist in outputs.tf" {
local outputs
outputs=$(_all_tf_output_names)
for name in cluster_name aurora_cluster_id aurora_endpoint aurora_password efs_id openemr_role_arn aurora_db_subnet_group_name aurora_engine_version; do
echo "$outputs" | grep -qx "$name" || {
echo "Missing Terraform output: $name (referenced in restore.sh)"
return 1
}
done
}
@test "CONTRACT: destroy.sh terraform outputs exist in outputs.tf" {
local outputs
outputs=$(_all_tf_output_names)
for name in cluster_name alb_logs_bucket_name loki_s3_bucket_name tempo_s3_bucket_name mimir_blocks_s3_bucket_name alertmanager_s3_bucket_name backup_vault_name; do
echo "$outputs" | grep -qx "$name" || {
echo "Missing Terraform output: $name (referenced in destroy.sh)"
return 1
}
done
}
# ===========================================================================
# K8S MANIFEST CONSISTENCY
# ===========================================================================
@test "CONTRACT: all credential rotation manifests use same ServiceAccount name" {
local sa_name="credential-rotation-sa"
grep -q "name: $sa_name" "$K8S_DIR/credential-rotation-sa.yaml"
grep -q "serviceAccountName: $sa_name" "$K8S_DIR/credential-rotation-job.yaml"
grep -q "serviceAccountName: $sa_name" "$K8S_DIR/credential-rotation-cronjob.yaml"
grep -q "name: $sa_name" "$K8S_DIR/credential-rotation-rbac.yaml"
}
@test "CONTRACT: all credential rotation manifests use namespace 'openemr'" {
for f in credential-rotation-sa.yaml credential-rotation-rbac.yaml credential-rotation-job.yaml credential-rotation-cronjob.yaml; do
grep -q "namespace: openemr" "$K8S_DIR/$f" || {
echo "Missing namespace: openemr in $f"
return 1
}
done
}
@test "CONTRACT: credential rotation Job and CronJob use same container image variable" {
local job_image cronjob_image
job_image=$(grep 'image:' "$K8S_DIR/credential-rotation-job.yaml" | head -1 | awk '{print $2}')
cronjob_image=$(grep 'image:' "$K8S_DIR/credential-rotation-cronjob.yaml" | head -1 | awk '{print $2}')
[ "$job_image" = "$cronjob_image" ]
}
@test "CONTRACT: credential rotation Job and CronJob use same env vars" {
local job_envs cronjob_envs
job_envs=$(grep '- name:' "$K8S_DIR/credential-rotation-job.yaml" | awk '{print $3}' | sort)
cronjob_envs=$(grep '- name:' "$K8S_DIR/credential-rotation-cronjob.yaml" | awk '{print $3}' | sort)
[ "$job_envs" = "$cronjob_envs" ]
}
@test "CONTRACT: RBAC targets openemr-db-credentials Secret (matches secrets.yaml)" {
grep -q 'openemr-db-credentials' "$K8S_DIR/credential-rotation-rbac.yaml"
grep -q 'name: openemr-db-credentials' "$K8S_DIR/secrets.yaml"
}
@test "CONTRACT: RBAC targets 'openemr' Deployment (matches deployment.yaml)" {
grep -q 'resourceNames: \["openemr"\]' "$K8S_DIR/credential-rotation-rbac.yaml" || \
grep -q '"openemr"' "$K8S_DIR/credential-rotation-rbac.yaml"
grep -q 'name: openemr' "$K8S_DIR/deployment.yaml"
}
@test "CONTRACT: Job K8S_SECRET_NAME env matches secrets.yaml Secret name" {
local job_secret_name
job_secret_name=$(grep -A1 'K8S_SECRET_NAME' "$K8S_DIR/credential-rotation-job.yaml" | grep 'value:' | awk -F'"' '{print $2}')
grep -q "name: $job_secret_name" "$K8S_DIR/secrets.yaml"
}
@test "CONTRACT: all credential rotation manifests have consistent labels" {
for f in credential-rotation-sa.yaml credential-rotation-rbac.yaml credential-rotation-job.yaml credential-rotation-cronjob.yaml; do
grep -q 'app: credential-rotation' "$K8S_DIR/$f" || {
echo "Missing label app: credential-rotation in $f"
return 1
}
done
}
@test "CONTRACT: Job references openemr-sites-pvc PVC (must exist in storage.yaml)" {
grep -q 'claimName: openemr-sites-pvc' "$K8S_DIR/credential-rotation-job.yaml"
grep -q 'openemr-sites-pvc' "$K8S_DIR/storage.yaml"
}
# ===========================================================================
# SCRIPT-TO-FILE REFERENCES
# ===========================================================================
@test "CONTRACT: run-credential-rotation.sh references existing K8s manifests" {
local script="${SCRIPTS_DIR}/run-credential-rotation.sh"
for manifest in credential-rotation-rbac.yaml credential-rotation-sa.yaml credential-rotation-job.yaml; do
if grep -q "$manifest" "$script"; then
[ -f "$K8S_DIR/$manifest" ] || {
echo "Referenced manifest $manifest does not exist"
return 1
}
fi
done
}
@test "CONTRACT: every k8s/*.yaml file is valid YAML (parseable)" {
for f in "$K8S_DIR"/*.yaml; do
python3 -c "import yaml; yaml.safe_load_all(open('$f'))" 2>/dev/null || {
echo "Invalid YAML: $f"
return 1
}
done
}
@test "CONTRACT: k8s deployment.yaml and service.yaml share 'app: openemr' selector" {
grep -q 'app: openemr' "$K8S_DIR/deployment.yaml"
grep -q 'app: openemr' "$K8S_DIR/service.yaml"
}
@test "CONTRACT: HPA targets 'openemr' Deployment (matches deployment.yaml)" {
grep -q 'name: openemr' "$K8S_DIR/hpa.yaml"
grep -q 'name: openemr' "$K8S_DIR/deployment.yaml"
}
@test "CONTRACT: all k8s manifests in openemr namespace use it consistently" {
for f in deployment.yaml service.yaml secrets.yaml hpa.yaml storage.yaml; do
if grep -q 'namespace:' "$K8S_DIR/$f"; then
grep 'namespace:' "$K8S_DIR/$f" | grep -q 'openemr' || {
echo "$f has a non-openemr namespace"
return 1
}
fi
done
}
# ===========================================================================
# DOCKERFILE & TOOL CONSISTENCY
# ===========================================================================
@test "CONTRACT: credential rotation Dockerfile installs from requirements.txt" {
grep -q 'requirements.txt' "$CRED_ROT_DOCKERFILE"
}
@test "CONTRACT: credential rotation Dockerfile uses -slim base image" {
grep -q 'python:.*-slim' "$CRED_ROT_DOCKERFILE"
}